feat: shayegan inbound charges workflow and copper kilogram unit enforcement
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Services\BalanceReportService;
|
|||||||
use App\Services\ConfigService;
|
use App\Services\ConfigService;
|
||||||
use App\Services\CustomerPresenceService;
|
use App\Services\CustomerPresenceService;
|
||||||
use App\Services\ReferralGiftService;
|
use App\Services\ReferralGiftService;
|
||||||
|
use App\Services\ShayeganSyncService;
|
||||||
use App\Services\WalletBalanceService;
|
use App\Services\WalletBalanceService;
|
||||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
|
|
||||||
@@ -198,6 +199,23 @@ class CustomerController extends SuperController
|
|||||||
|
|
||||||
$updated = $data === [] ? false : $customer->update($data);
|
$updated = $data === [] ? false : $customer->update($data);
|
||||||
|
|
||||||
|
// همگامسازی با شایگان در صورت تغییر دستی کد حسابداری
|
||||||
|
if (array_key_exists('moshtari_code', $data) && app(ConfigService::class)->isShayeganAccounting()) {
|
||||||
|
$newCode = $data['moshtari_code'];
|
||||||
|
if ($newCode !== null && $newCode !== '') {
|
||||||
|
try {
|
||||||
|
$shayeganSync = app(ShayeganSyncService::class);
|
||||||
|
$shayeganSync->registerCustomer($customer, $newCode);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Illuminate\Support\Facades\Log::warning('shayegan.manual_code_sync_failed', [
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'code' => $newCode,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'g' => $goldUpdated,
|
'g' => $goldUpdated,
|
||||||
'u' => $updated,
|
'u' => $updated,
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ namespace App\Http\Controllers\Admin;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Shayegan;
|
use App\Models\Shayegan;
|
||||||
|
use App\Models\ShayeganInboundCharge;
|
||||||
|
use App\Services\ConfigService;
|
||||||
use App\Services\ShayeganClient;
|
use App\Services\ShayeganClient;
|
||||||
|
use App\Services\ShayeganInboundChargeService;
|
||||||
use App\Services\ShayeganSyncService;
|
use App\Services\ShayeganSyncService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -88,7 +91,7 @@ class ShayeganController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = Shayegan::query()->orderByDesc('id');
|
$query = Shayegan::with('customer')->orderByDesc('id');
|
||||||
\App\DB\Te\TableEngine::create($query, $request)
|
\App\DB\Te\TableEngine::create($query, $request)
|
||||||
->initSearch(['desc', 'type', 'shayegan_number', 'sync_status'])
|
->initSearch(['desc', 'type', 'shayegan_number', 'sync_status'])
|
||||||
->select(['*'])
|
->select(['*'])
|
||||||
@@ -99,7 +102,7 @@ class ShayeganController extends Controller
|
|||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
$row = Shayegan::findOrFail($id);
|
$row = Shayegan::with('customer')->findOrFail($id);
|
||||||
|
|
||||||
return response()->json($row);
|
return response()->json($row);
|
||||||
}
|
}
|
||||||
@@ -163,6 +166,132 @@ class ShayeganController extends Controller
|
|||||||
return response()->json(array_merge(['ok' => false, 'message' => $row->sync_log, 'row' => $row], $exchange), 422);
|
return response()->json(array_merge(['ok' => false, 'message' => $row->sync_log, 'row' => $row], $exchange), 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* وبهوک دریافت شارژ حساب مشتری از پنل حسابداری شایگان
|
||||||
|
*/
|
||||||
|
public function inboundWebhook(Request $request, ShayeganInboundChargeService $inboundService)
|
||||||
|
{
|
||||||
|
$secret = app(ConfigService::class)->getString('shayegan_webhook_secret', '');
|
||||||
|
$key = $request->header('X-Shayegan-Key') ?: $request->input('secret');
|
||||||
|
|
||||||
|
if ($secret !== '' && $key !== $secret) {
|
||||||
|
return response()->json([
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'کلید امنیتی وبهوک شایگان نامعتبر است (X-Shayegan-Key).',
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $inboundService->registerCharge($request->all(), 'webhook');
|
||||||
|
|
||||||
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* دریافت لیست شارژهای در انتظار تایید برای نمایش اعلان در پنل ادمین
|
||||||
|
*/
|
||||||
|
public function pendingCharges()
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('shayegan_inbound_charges')) {
|
||||||
|
return response()->json(['ok' => true, 'charges' => []]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$charges = ShayeganInboundCharge::with('customer')
|
||||||
|
->where('status', 'pending')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->limit(20)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'ok' => true,
|
||||||
|
'charges' => $charges,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* جدول شارژهای دریافتی از شایگان
|
||||||
|
*/
|
||||||
|
public function chargesTable(Request $request)
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('shayegan_inbound_charges')) {
|
||||||
|
return response()->json([
|
||||||
|
'rows' => [],
|
||||||
|
'query' => [
|
||||||
|
'limit' => (int) $request->input('limit', 20),
|
||||||
|
'page' => 1,
|
||||||
|
'total' => 0,
|
||||||
|
'filters' => $request->input('filters', []),
|
||||||
|
'search' => $request->input('search'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = ShayeganInboundCharge::with(['customer', 'approver'])->orderByDesc('id');
|
||||||
|
|
||||||
|
if ($request->filled('status')) {
|
||||||
|
$query->where('status', $request->input('status'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->filled('search')) {
|
||||||
|
$s = trim((string) $request->input('search'));
|
||||||
|
$query->where(function ($q) use ($s) {
|
||||||
|
$q->where('shayegan_number', 'like', "%{$s}%")
|
||||||
|
->orWhere('customer_tafsil', 'like', "%{$s}%")
|
||||||
|
->orWhereHas('customer', function ($cq) use ($s) {
|
||||||
|
$cq->where('name', 'like', "%{$s}%")
|
||||||
|
->orWhere('mobile', 'like', "%{$s}%")
|
||||||
|
->orWhere('n_code', 'like', "%{$s}%");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = max(1, (int) $request->input('limit', 20));
|
||||||
|
$page = max(1, (int) $request->input('page', 1));
|
||||||
|
$total = $query->count();
|
||||||
|
$rows = $query->skip(($page - 1) * $limit)->take($limit)->get();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'rows' => $rows,
|
||||||
|
'query' => [
|
||||||
|
'page' => $page,
|
||||||
|
'limit' => $limit,
|
||||||
|
'total' => $total,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* تایید شارژ و افزایش کیف پول مشتری در بورس مس
|
||||||
|
*/
|
||||||
|
public function approveCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
|
||||||
|
{
|
||||||
|
$charge = ShayeganInboundCharge::with('customer')->findOrFail($id);
|
||||||
|
$res = $inboundService->approveCharge($charge, auth()->user());
|
||||||
|
|
||||||
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* رد درخواست شارژ
|
||||||
|
*/
|
||||||
|
public function rejectCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
|
||||||
|
{
|
||||||
|
$charge = ShayeganInboundCharge::findOrFail($id);
|
||||||
|
$reason = $request->input('reason');
|
||||||
|
$res = $inboundService->rejectCharge($charge, auth()->user(), $reason);
|
||||||
|
|
||||||
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* استعلام اسناد جدید از شایگان
|
||||||
|
*/
|
||||||
|
public function syncChargesFromShayegan(ShayeganInboundChargeService $inboundService)
|
||||||
|
{
|
||||||
|
$res = $inboundService->pollFromShayegan();
|
||||||
|
|
||||||
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $body
|
* @param mixed $body
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ class Controller extends BaseController
|
|||||||
return self::isMainWalletProduct($productId) ? null : $productId;
|
return self::isMainWalletProduct($productId) ? null : $productId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** واحد وزن معاملات فلز — از ستینگ unit؛ پیشفرض گرم */
|
/** واحد وزن معاملات فلز — در بورس مس پیشفرض کیلوگرم است */
|
||||||
public static function tradeWeightUnit(): string
|
public static function tradeWeightUnit(): string
|
||||||
{
|
{
|
||||||
return self::resolveTradeWeightUnit(
|
return self::resolveTradeWeightUnit(
|
||||||
@@ -512,11 +512,11 @@ class Controller extends BaseController
|
|||||||
{
|
{
|
||||||
$unit = trim((string) $value);
|
$unit = trim((string) $value);
|
||||||
|
|
||||||
if ($unit === 'کیلوگرم' || $unit === 'kilogram' || $unit === 'kg') {
|
if ($unit === 'گرم' || $unit === 'gram' || $unit === 'g') {
|
||||||
return 'کیلوگرم';
|
return 'گرم';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'گرم';
|
return 'کیلوگرم';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function weightToGrams(float $value, ?string $fromUnit = null): float
|
public static function weightToGrams(float $value, ?string $fromUnit = null): float
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class Shayegan extends Model
|
class Shayegan extends Model
|
||||||
{
|
{
|
||||||
@@ -14,4 +15,10 @@ class Shayegan extends Model
|
|||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'amount' => 'float',
|
'amount' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function customer(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Customer::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class ShayeganInboundCharge extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'shayegan_inbound_charges';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'customer_id',
|
||||||
|
'customer_tafsil',
|
||||||
|
'shayegan_number',
|
||||||
|
'amount',
|
||||||
|
'description',
|
||||||
|
'source',
|
||||||
|
'status',
|
||||||
|
'raw_data',
|
||||||
|
'approved_by',
|
||||||
|
'approved_at',
|
||||||
|
'rejected_by',
|
||||||
|
'rejected_at',
|
||||||
|
'reject_reason',
|
||||||
|
'withdraw_id',
|
||||||
|
'transaction_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'amount' => 'float',
|
||||||
|
'approved_at' => 'datetime',
|
||||||
|
'rejected_at' => 'datetime',
|
||||||
|
'raw_data' => 'array',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function customer()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Customer::class, 'customer_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function approver()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'approved_by');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rejecter()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'rejected_by');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withdraw()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Withdraw::class, 'withdraw_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -717,6 +717,8 @@ class AssetSummaryService
|
|||||||
$meltedWeight = round(Controller::gramsToTradeUnit($meltedGrams), 3);
|
$meltedWeight = round(Controller::gramsToTradeUnit($meltedGrams), 3);
|
||||||
$totalWeight = round($productWeight + $meltedWeight, 3);
|
$totalWeight = round($productWeight + $meltedWeight, 3);
|
||||||
|
|
||||||
|
$metalUnit = ((int) $metalId === 3 || $weightUnit === 'کیلوگرم') ? 'کیلوگرم' : $weightUnit;
|
||||||
|
|
||||||
$totals[] = [
|
$totals[] = [
|
||||||
'metal_id' => (int) $metalId,
|
'metal_id' => (int) $metalId,
|
||||||
'metal_label' => self::METAL_LABELS[$metalId] ?? 'فلز',
|
'metal_label' => self::METAL_LABELS[$metalId] ?? 'فلز',
|
||||||
@@ -724,7 +726,7 @@ class AssetSummaryService
|
|||||||
'product_weight' => $productWeight,
|
'product_weight' => $productWeight,
|
||||||
'melted_weight' => $meltedWeight,
|
'melted_weight' => $meltedWeight,
|
||||||
'total_weight' => $totalWeight,
|
'total_weight' => $totalWeight,
|
||||||
'unit' => $weightUnit,
|
'unit' => $metalUnit,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,367 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Customer;
|
||||||
|
use App\Models\ShayeganInboundCharge;
|
||||||
|
use App\Models\Transaction;
|
||||||
|
use App\Models\TransactionsAdmin;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Withdraw;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||||
|
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
|
|
||||||
|
class ShayeganInboundChargeService
|
||||||
|
{
|
||||||
|
private ConfigService $config;
|
||||||
|
private ShayeganClient $client;
|
||||||
|
private WalletBroadcastService $walletBroadcast;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ConfigService $config,
|
||||||
|
ShayeganClient $client,
|
||||||
|
WalletBroadcastService $walletBroadcast
|
||||||
|
) {
|
||||||
|
$this->config = $config;
|
||||||
|
$this->client = $client;
|
||||||
|
$this->walletBroadcast = $walletBroadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* پیدا کردن مشتری با اولویت: شناسه مستقیم، کد تفصیلی، موبایل، کد ملی
|
||||||
|
*/
|
||||||
|
public function resolveCustomer(array $params): ?Customer
|
||||||
|
{
|
||||||
|
// 1. با شناسه مشتری در بورس مس
|
||||||
|
if (! empty($params['customer_id'])) {
|
||||||
|
$customer = Customer::find((int) $params['customer_id']);
|
||||||
|
if ($customer) {
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. با کد تفصیلی شایگان (moshtari_code)
|
||||||
|
$tafsil = trim((string) ($params['customer_tafsil'] ?? $params['moshtari_code'] ?? $params['job_number'] ?? ''));
|
||||||
|
if ($tafsil !== '') {
|
||||||
|
$customer = Customer::where('moshtari_code', $tafsil)->first();
|
||||||
|
if ($customer) {
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی فرمول احتمالی کد کل + کد کاربر ۷ رقمی
|
||||||
|
$kol = trim($this->config->getString('shayegan_kol_customer', '121'));
|
||||||
|
if ($kol !== '' && str_starts_with($tafsil, $kol)) {
|
||||||
|
$subId = (int) substr($tafsil, strlen($kol));
|
||||||
|
if ($subId > 0) {
|
||||||
|
$customer = Customer::find($subId);
|
||||||
|
if ($customer) {
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. با شماره موبایل
|
||||||
|
$mobile = trim((string) ($params['mobile'] ?? ''));
|
||||||
|
if ($mobile !== '') {
|
||||||
|
$customer = Customer::findActiveByMobile($mobile);
|
||||||
|
if ($customer) {
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. با کد ملی
|
||||||
|
$nCode = trim((string) ($params['n_code'] ?? $params['national_code'] ?? $params['mellicode'] ?? ''));
|
||||||
|
if ($nCode !== '') {
|
||||||
|
$customer = Customer::where('n_code', $nCode)->first();
|
||||||
|
if ($customer) {
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ثبت درخواست شارژ ورودی از شایگان در وضعیت pending
|
||||||
|
*/
|
||||||
|
public function registerCharge(array $data, string $source = 'webhook'): array
|
||||||
|
{
|
||||||
|
$customer = $this->resolveCustomer($data);
|
||||||
|
if (! $customer) {
|
||||||
|
return [
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'مشتری با مشخصات ارسالی (کد مشتری، کد تفصیلی، موبایل یا کد ملی) یافت نشد.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$amount = (float) ($data['amount'] ?? $data['value'] ?? $data['RialBes'] ?? $data['AmountBes'] ?? 0);
|
||||||
|
if ($amount <= 0) {
|
||||||
|
return [
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'مبلغ شارژ باید مقداری مثبت و بزرگتر از صفر باشد.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$shayeganNumber = trim((string) ($data['shayegan_number'] ?? $data['Number'] ?? $data['doc_number'] ?? ''));
|
||||||
|
$tafsil = trim((string) ($data['customer_tafsil'] ?? $data['moshtari_code'] ?? $customer->moshtari_code ?? ''));
|
||||||
|
$description = trim((string) ($data['description'] ?? $data['desc'] ?? $data['note'] ?? 'شارژ از طریق پنل حسابداری شایگان'));
|
||||||
|
|
||||||
|
// جلوگیری از ثبت تکراری اگر قبلاً سند با همین شماره برای این مشتری ثبت شده است
|
||||||
|
if ($shayeganNumber !== '') {
|
||||||
|
$existing = ShayeganInboundCharge::where('shayegan_number', $shayeganNumber)
|
||||||
|
->where('customer_id', $customer->id)
|
||||||
|
->first();
|
||||||
|
if ($existing) {
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => 'این سند شایگان قبلاً ثبت شده است.',
|
||||||
|
'charge' => $existing->load('customer'),
|
||||||
|
'duplicate' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$charge = ShayeganInboundCharge::create([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'customer_tafsil' => $tafsil ?: null,
|
||||||
|
'shayegan_number' => $shayeganNumber ?: null,
|
||||||
|
'amount' => $amount,
|
||||||
|
'description' => $description,
|
||||||
|
'source' => $source,
|
||||||
|
'status' => 'pending',
|
||||||
|
'raw_data' => $data,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('shayegan.inbound_charge_registered', [
|
||||||
|
'charge_id' => $charge->id,
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'amount' => $amount,
|
||||||
|
'shayegan_number' => $shayeganNumber,
|
||||||
|
'source' => $source,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => "حساب شخص {$customer->name} از طریق پنل حسابداری مقدار " . number_format($amount) . " ریال شارژ شد.",
|
||||||
|
'charge' => $charge->load('customer'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* تایید شارژ و افزایش کیف پول ریالی مشتری در بورس مس
|
||||||
|
*/
|
||||||
|
public function approveCharge(ShayeganInboundCharge $charge, ?User $adminUser = null): array
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($charge, $adminUser) {
|
||||||
|
$charge = ShayeganInboundCharge::where('id', $charge->id)->lockForUpdate()->firstOrFail();
|
||||||
|
|
||||||
|
if ($charge->status === 'approved') {
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => 'این شارژ قبلاً تایید شده است.',
|
||||||
|
'charge' => $charge->load(['customer', 'approver']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($charge->status !== 'pending') {
|
||||||
|
throw new UnprocessableEntityHttpException('وضعیت این شارژ برای تایید معتبر نیست (' . $charge->status . ').');
|
||||||
|
}
|
||||||
|
|
||||||
|
$customer = $charge->customer;
|
||||||
|
if (! $customer) {
|
||||||
|
throw new UnprocessableEntityHttpException('مشتری مرتبط با این شارژ یافت نشد.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$adminId = $adminUser ? (int) $adminUser->id : 1;
|
||||||
|
$note = $charge->description ?: ('شارژ تایید شده از پنل حسابداری شایگان' . ($charge->shayegan_number ? " (سند {$charge->shayegan_number})" : ''));
|
||||||
|
|
||||||
|
// 1. ثبت Withdraw (واریز ریالی با sts = 4)
|
||||||
|
$withdraw = Withdraw::create([
|
||||||
|
'user_id' => $adminId,
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'cash' => $charge->amount,
|
||||||
|
'note' => $note,
|
||||||
|
'sts' => 4,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. افزایش موجودی در کیف پول (Rooyesh Wallet)
|
||||||
|
$walletInterface = new WalletInterface((int) $customer->id, (int) $customer->type);
|
||||||
|
new TransactionInterface($walletInterface, [
|
||||||
|
'gold' => 0,
|
||||||
|
'cash' => $charge->amount,
|
||||||
|
'expire_at' => now(),
|
||||||
|
'note' => $note,
|
||||||
|
'withdraw_id' => $withdraw->id,
|
||||||
|
'active' => true,
|
||||||
|
], 3, true, true, 0); // type 3 = deposit, deposit = true
|
||||||
|
|
||||||
|
// 3. ثبت لاگ تراکنش ادمین
|
||||||
|
$lastTx = Transaction::where('withdraw_id', $withdraw->id)->latest('id')->first();
|
||||||
|
TransactionsAdmin::create([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'customer_to_id' => $customer->id,
|
||||||
|
'user_id' => $adminId,
|
||||||
|
'wallet' => null,
|
||||||
|
'transaction_id' => $lastTx?->id,
|
||||||
|
'cash' => $charge->amount,
|
||||||
|
'note' => $note,
|
||||||
|
'type' => 1,
|
||||||
|
'gold' => 0,
|
||||||
|
'balance_cash' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. بهروزرسانی وضعیت رکورد شارژ شایگان
|
||||||
|
$charge->status = 'approved';
|
||||||
|
$charge->approved_by = $adminId;
|
||||||
|
$charge->approved_at = now();
|
||||||
|
$charge->withdraw_id = $withdraw->id;
|
||||||
|
$charge->transaction_id = $lastTx?->id;
|
||||||
|
$charge->save();
|
||||||
|
|
||||||
|
// 5. برودکست ریلتایم موجودی کیف پول کاربر
|
||||||
|
try {
|
||||||
|
$this->walletBroadcast->broadcastForCustomer((int) $customer->id, (int) $customer->type);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('shayegan.wallet_broadcast_failed', ['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. تریگر اتوماسیون
|
||||||
|
try {
|
||||||
|
\App\Services\Automation\AutomationEvents::fire('wallet.rial_charge', (int) $customer->id);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('shayegan.automation_failed', ['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('shayegan.inbound_charge_approved', [
|
||||||
|
'charge_id' => $charge->id,
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'amount' => $charge->amount,
|
||||||
|
'withdraw_id' => $withdraw->id,
|
||||||
|
'admin_id' => $adminId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => "کیف پول شخص {$customer->name} به مبلغ " . number_format($charge->amount) . " ریال با موفقیت شارژ شد.",
|
||||||
|
'charge' => $charge->load(['customer', 'approver']),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* رد درخواست شارژ
|
||||||
|
*/
|
||||||
|
public function rejectCharge(ShayeganInboundCharge $charge, ?User $adminUser = null, ?string $reason = null): array
|
||||||
|
{
|
||||||
|
if ($charge->status === 'approved') {
|
||||||
|
throw new UnprocessableEntityHttpException('شارژ تایید شده را نمیتوان رد کرد.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$adminId = $adminUser ? (int) $adminUser->id : 1;
|
||||||
|
$charge->status = 'rejected';
|
||||||
|
$charge->rejected_by = $adminId;
|
||||||
|
$charge->rejected_at = now();
|
||||||
|
$charge->reject_reason = $reason;
|
||||||
|
$charge->save();
|
||||||
|
|
||||||
|
Log::info('shayegan.inbound_charge_rejected', [
|
||||||
|
'charge_id' => $charge->id,
|
||||||
|
'customer_id' => $charge->customer_id,
|
||||||
|
'admin_id' => $adminId,
|
||||||
|
'reason' => $reason,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => 'درخواست شارژ رد شد.',
|
||||||
|
'charge' => $charge->load(['customer', 'rejecter']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* استعلام اسناد جدید از وبسرویس شایگان (Sync / Polling)
|
||||||
|
*/
|
||||||
|
public function pollFromShayegan(): array
|
||||||
|
{
|
||||||
|
if (! $this->client->isConfigured()) {
|
||||||
|
return [
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'تنظیمات اتصال وبسرویس شایگان در سیستم پیکربندی نشده است.',
|
||||||
|
'new_count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// استعلام از وبسرویس شایگان (مانند Document/Get با فیلتر بستانکاری تفصیلی مشتریان)
|
||||||
|
$res = $this->client->get('Document/Get', [], 0, 50);
|
||||||
|
if (empty($res['ok']) || empty($res['body']['Result']) || ! is_array($res['body']['Result'])) {
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => 'سند جدیدی در استعلام شایگان یافت نشد.',
|
||||||
|
'new_count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach ($res['body']['Result'] as $doc) {
|
||||||
|
$docNumber = (string) ($doc['Number'] ?? $doc['DocNumber'] ?? '');
|
||||||
|
if ($docNumber === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $doc['Body'] ?? [];
|
||||||
|
if (! is_array($rows)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$amountBes = (float) ($row['AmountBes'] ?? $row['RialBes'] ?? 0);
|
||||||
|
$accountNumber = trim((string) ($row['AccountNumber'] ?? ''));
|
||||||
|
if ($amountBes <= 0 || $accountNumber === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// جستجوی مشتری با شماره تفصیلی
|
||||||
|
$customer = Customer::where('moshtari_code', $accountNumber)->first();
|
||||||
|
if (! $customer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// آیا قبلا این شماره سند ثبت شده؟
|
||||||
|
$already = ShayeganInboundCharge::where('shayegan_number', $docNumber)
|
||||||
|
->where('customer_id', $customer->id)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if (! $already) {
|
||||||
|
$this->registerCharge([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'customer_tafsil' => $accountNumber,
|
||||||
|
'shayegan_number' => $docNumber,
|
||||||
|
'amount' => $amountBes,
|
||||||
|
'description' => (string) ($row['DocRowDesc'] ?? $doc['DocDescription'] ?? 'استعلام خودکار از شایگان'),
|
||||||
|
], 'sync');
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => true,
|
||||||
|
'message' => $count > 0 ? "تعداد {$count} شارژ جدید از شایگان واکشی شد." : 'سند شارژ جدیدی یافت نشد.',
|
||||||
|
'new_count' => $count,
|
||||||
|
];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('shayegan.poll_failed', ['error' => $e->getMessage()]);
|
||||||
|
return [
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'خطا در ارتباط با وبسرویس شایگان: ' . $e->getMessage(),
|
||||||
|
'new_count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -94,11 +94,16 @@ class ShayeganSyncService
|
|||||||
/**
|
/**
|
||||||
* @return array{ok:bool,message:string,moshtari_code?:string}
|
* @return array{ok:bool,message:string,moshtari_code?:string}
|
||||||
*/
|
*/
|
||||||
public function registerCustomer(Customer $customer): array
|
public function registerCustomer(Customer $customer, ?string $customJobNumber = null): array
|
||||||
{
|
{
|
||||||
$customer->refresh();
|
$customer->refresh();
|
||||||
|
|
||||||
if (! empty($customer->moshtari_code)) {
|
$customJobNumber = $customJobNumber !== null ? trim($customJobNumber) : null;
|
||||||
|
if ($customJobNumber === '') {
|
||||||
|
$customJobNumber = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($customJobNumber === null && ! empty($customer->moshtari_code)) {
|
||||||
return [
|
return [
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'message' => 'مشتری قبلاً در شایگان ثبت شده است',
|
'message' => 'مشتری قبلاً در شایگان ثبت شده است',
|
||||||
@@ -119,8 +124,8 @@ class ShayeganSyncService
|
|||||||
$name = 'مشتری ' . $customer->id;
|
$name = 'مشتری ' . $customer->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// تفصیلی شناور: کل ثابت + کد کاربری ۷رقمی. مشتری ۱ → 1210000001
|
// تفصیلی شناور: اگر کد دستی ارسال شده باشد همان کد استفاده میشود، در غیر این صورت فرمول پیشفرض
|
||||||
$requestedAccountNumber = $this->customerTafsilNumber((int) $customer->id);
|
$requestedAccountNumber = $customJobNumber ?? $this->customerTafsilNumber((int) $customer->id);
|
||||||
|
|
||||||
$guid = (string) Str::uuid();
|
$guid = (string) Str::uuid();
|
||||||
$put = [[
|
$put = [[
|
||||||
@@ -138,9 +143,22 @@ class ShayeganSyncService
|
|||||||
|
|
||||||
$result = $this->client->put('Job/Put', $put);
|
$result = $this->client->put('Job/Put', $put);
|
||||||
$accountNumber = trim((string) ($result['number'] ?? ''));
|
$accountNumber = trim((string) ($result['number'] ?? ''));
|
||||||
|
if ($accountNumber === '' && $customJobNumber !== null && ! empty($result['ok'])) {
|
||||||
|
$accountNumber = $customJobNumber;
|
||||||
|
}
|
||||||
$exchange = $this->client->debugExchange($result);
|
$exchange = $this->client->debugExchange($result);
|
||||||
|
|
||||||
if (! empty($result['timeout'])) {
|
if (! empty($result['timeout'])) {
|
||||||
|
$this->logCustomerRegistrationDoc(
|
||||||
|
$customer,
|
||||||
|
$requestedAccountNumber,
|
||||||
|
$guid,
|
||||||
|
$put,
|
||||||
|
$result,
|
||||||
|
self::STATUS_TIMEOUT,
|
||||||
|
(string) ($result['message'] ?? 'timeout')
|
||||||
|
);
|
||||||
|
|
||||||
return array_merge([
|
return array_merge([
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => (string) ($result['message'] ?? 'timeout'),
|
'message' => (string) ($result['message'] ?? 'timeout'),
|
||||||
@@ -148,15 +166,56 @@ class ShayeganSyncService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($result['ok']) || $accountNumber === '') {
|
if (empty($result['ok']) || $accountNumber === '') {
|
||||||
|
$errorMsg = (string) ($result['message'] ?? 'ثبت حساب مشتری در شایگان ناموفق بود');
|
||||||
|
$this->logCustomerRegistrationDoc(
|
||||||
|
$customer,
|
||||||
|
$requestedAccountNumber,
|
||||||
|
$guid,
|
||||||
|
$put,
|
||||||
|
$result,
|
||||||
|
self::STATUS_FAILED,
|
||||||
|
$errorMsg
|
||||||
|
);
|
||||||
|
|
||||||
return array_merge([
|
return array_merge([
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => (string) ($result['message'] ?? 'ثبت حساب مشتری در شایگان ناموفق بود'),
|
'message' => $errorMsg,
|
||||||
], $exchange);
|
], $exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
$customer->moshtari_code = $accountNumber;
|
$customer->moshtari_code = $accountNumber;
|
||||||
$customer->save();
|
$customer->save();
|
||||||
|
|
||||||
|
$this->logCustomerRegistrationDoc(
|
||||||
|
$customer,
|
||||||
|
$accountNumber,
|
||||||
|
$guid,
|
||||||
|
$put,
|
||||||
|
$result,
|
||||||
|
self::STATUS_SUCCESS,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_merge([
|
||||||
|
'ok' => true,
|
||||||
|
'message' => 'تفصیلی شناور مشتری در شایگان ایجاد شد',
|
||||||
|
'moshtari_code' => $accountNumber,
|
||||||
|
], $exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, mixed> $put
|
||||||
|
* @param array<string, mixed> $result
|
||||||
|
*/
|
||||||
|
private function logCustomerRegistrationDoc(
|
||||||
|
Customer $customer,
|
||||||
|
string $accountNumber,
|
||||||
|
string $guid,
|
||||||
|
array $put,
|
||||||
|
array $result,
|
||||||
|
string $status,
|
||||||
|
?string $logMessage = null
|
||||||
|
): Shayegan {
|
||||||
$row = new Shayegan();
|
$row = new Shayegan();
|
||||||
$row->customer_id = (int) $customer->id;
|
$row->customer_id = (int) $customer->id;
|
||||||
$row->order_id = null;
|
$row->order_id = null;
|
||||||
@@ -170,7 +229,8 @@ class ShayeganSyncService
|
|||||||
$row->request_guid = $guid;
|
$row->request_guid = $guid;
|
||||||
$row->shayegan_number = $accountNumber;
|
$row->shayegan_number = $accountNumber;
|
||||||
$row->shayegan_guid = $result['guid'] ?? null;
|
$row->shayegan_guid = $result['guid'] ?? null;
|
||||||
$row->sync_status = self::STATUS_SUCCESS;
|
$row->sync_status = $status;
|
||||||
|
$row->sync_log = $logMessage;
|
||||||
$row->data = json_encode($this->client->redactPayload([
|
$row->data = json_encode($this->client->redactPayload([
|
||||||
'Config' => $this->client->buildConfig(),
|
'Config' => $this->client->buildConfig(),
|
||||||
'PutObject' => $put,
|
'PutObject' => $put,
|
||||||
@@ -178,11 +238,7 @@ class ShayeganSyncService
|
|||||||
$row->res = json_encode($result['body'] ?? [], JSON_UNESCAPED_UNICODE);
|
$row->res = json_encode($result['body'] ?? [], JSON_UNESCAPED_UNICODE);
|
||||||
$row->save();
|
$row->save();
|
||||||
|
|
||||||
return array_merge([
|
return $row;
|
||||||
'ok' => true,
|
|
||||||
'message' => 'تفصیلی شناور مشتری در شایگان ایجاد شد',
|
|
||||||
'moshtari_code' => $accountNumber,
|
|
||||||
], $exchange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('shayegan_inbound_charges')) {
|
||||||
|
Schema::create('shayegan_inbound_charges', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('customer_id')->nullable()->index();
|
||||||
|
$table->string('customer_tafsil', 64)->nullable()->index();
|
||||||
|
$table->string('shayegan_number', 64)->nullable()->index();
|
||||||
|
$table->decimal('amount', 18, 4);
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->string('source', 32)->default('webhook')->index();
|
||||||
|
$table->string('status', 20)->default('pending')->index(); // pending, approved, rejected
|
||||||
|
$table->longText('raw_data')->nullable();
|
||||||
|
$table->unsignedBigInteger('approved_by')->nullable()->index();
|
||||||
|
$table->timestamp('approved_at')->nullable();
|
||||||
|
$table->unsignedBigInteger('rejected_by')->nullable()->index();
|
||||||
|
$table->timestamp('rejected_at')->nullable();
|
||||||
|
$table->string('reject_reason', 255)->nullable();
|
||||||
|
$table->unsignedBigInteger('withdraw_id')->nullable()->index();
|
||||||
|
$table->unsignedBigInteger('transaction_id')->nullable()->index();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('shayegan_inbound_charges');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('settings')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exists = DB::table('settings')->where('key', 'shayegan_webhook_secret')->exists();
|
||||||
|
if (! $exists) {
|
||||||
|
$hasMenu = Schema::hasColumn('settings', 'menu');
|
||||||
|
$row = [
|
||||||
|
'key' => 'shayegan_webhook_secret',
|
||||||
|
'value' => 'shayegan_sec_' . bin2hex(random_bytes(12)),
|
||||||
|
'title_fa' => 'کلید امنیتی وبهوک شایگان (X-Shayegan-Key)',
|
||||||
|
'description' => 'کلید محرمانه جهت احراز هویت درخواستهای وبهوک شارژ از شایگان به بورس مس.',
|
||||||
|
'can_edit' => 0,
|
||||||
|
'is_active' => 1,
|
||||||
|
'type' => 'password',
|
||||||
|
];
|
||||||
|
if ($hasMenu) {
|
||||||
|
$row['menu'] = 'accounting_shayegan';
|
||||||
|
}
|
||||||
|
DB::table('settings')->insert($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('settings')) {
|
||||||
|
DB::table('settings')->where('key', 'shayegan_webhook_secret')->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('settings')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = DB::table('settings')->where('key', 'unit')->first();
|
||||||
|
if ($row) {
|
||||||
|
$val = trim((string) ($row->value ?? ''));
|
||||||
|
if ($val === '' || $val === 'گرم') {
|
||||||
|
DB::table('settings')->where('key', 'unit')->update([
|
||||||
|
'value' => 'کیلوگرم',
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DB::table('settings')->insert([
|
||||||
|
'key' => 'unit',
|
||||||
|
'value' => 'کیلوگرم',
|
||||||
|
'title_fa' => 'واحد وزن معاملات',
|
||||||
|
'description' => 'واحد اصلی وزن در بورس مس (کیلوگرم).',
|
||||||
|
'can_edit' => 0,
|
||||||
|
'is_active' => 1,
|
||||||
|
'type' => 'text',
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -302,6 +302,11 @@ Route::middleware(['auth:user', 'admin.page'])->group(function (){
|
|||||||
Route::get('shayegan/{id}', [ShayeganController::class, 'show']);
|
Route::get('shayegan/{id}', [ShayeganController::class, 'show']);
|
||||||
Route::post('shayegan/{id}/retry', [ShayeganController::class, 'retrySanad']);
|
Route::post('shayegan/{id}/retry', [ShayeganController::class, 'retrySanad']);
|
||||||
Route::post('customer/{id}/shayegan/register', [ShayeganController::class, 'registerCustomer']);
|
Route::post('customer/{id}/shayegan/register', [ShayeganController::class, 'registerCustomer']);
|
||||||
|
Route::get('shayegan/charges/pending', [ShayeganController::class, 'pendingCharges']);
|
||||||
|
Route::post('shayegan/charges/table', [ShayeganController::class, 'chargesTable']);
|
||||||
|
Route::post('shayegan/charges/{id}/approve', [ShayeganController::class, 'approveCharge']);
|
||||||
|
Route::post('shayegan/charges/{id}/reject', [ShayeganController::class, 'rejectCharge']);
|
||||||
|
Route::post('shayegan/charges/sync', [ShayeganController::class, 'syncChargesFromShayegan']);
|
||||||
|
|
||||||
Route::post('pargar/table', [PargarController::class, 'table']);
|
Route::post('pargar/table', [PargarController::class, 'table']);
|
||||||
Route::get('pargar/customer-sync/status', [PargarController::class, 'customerSyncStatus']);
|
Route::get('pargar/customer-sync/status', [PargarController::class, 'customerSyncStatus']);
|
||||||
|
|||||||
@@ -71,3 +71,6 @@ if (app(ConfigService::class)->isStore()) {
|
|||||||
Route::post('/cart/remove', [CartController::class, 'removeItem']);
|
Route::post('/cart/remove', [CartController::class, 'removeItem']);
|
||||||
Route::get('categories', [CategoryController::class, 'clientIndex']);
|
Route::get('categories', [CategoryController::class, 'clientIndex']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// وبهوک دریافت شارژ مشتری از شایگان
|
||||||
|
Route::post('/shayegan/webhook/inbound-charge', [\App\Http\Controllers\Admin\ShayeganController::class, 'inboundWebhook']);
|
||||||
|
|||||||
Reference in New Issue
Block a user