Files
boursecu_server/app/Services/MontzarService.php
T
mohmdsdgandCursor 0b1e5c687b feat(order): wait for colleague callback before settling wallet
Keep cover orders pending until colleague sts 2/4/6 via callback or 35s poll. Nightly prices:prune also drops old log tables.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 15:55:03 +03:30

908 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Http\Controllers\Controller;
use App\Models\Cnt;
use App\Models\Order;
use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
class MontzarService
{
private function config(): ConfigService
{
return app(ConfigService::class);
}
private function baseUrl(): string
{
return rtrim(
$this->config()->Config('montzar_base_url') ?: 'https://montpar.ir',
'/'
);
}
private function endpoint(string $path): string
{
return $this->baseUrl() . '/' . ltrim($path, '/');
}
/** Pending HTTP با همان گزینه‌های پایدار (بدون پروکسی / IPv4 / HTTP1.1) */
private function http(float $timeout = 10)
{
$extraCurl = defined('CURLOPT_TCP_FASTOPEN')
? [(int) constant('CURLOPT_TCP_FASTOPEN') => 1]
: [];
return Http::timeout($timeout)
->acceptJson()
->withOptions(Controller::httpClientOptions($extraCurl, $timeout));
}
public function montzarLogin(): string
{
try {
$response = $this->http(10)
->post($this->endpoint('/server/auth/login'), [
'mobile' => $this->config()->Config('montzar_mobile'),
'password' => $this->config()->Config('montzar_password'),
'provider' => 'customer',
'type_app' => 'client',
])
->json();
} catch (\Throwable $e) {
Log::error('Montzar login request failed', [
'base_url' => $this->baseUrl(),
'error' => $e->getMessage(),
]);
throw new UnprocessableEntityHttpException(
'سرویس موقتا در دسترس نیست، لطفاً با پشتیبانی تماس بگیرید.'
);
}
$token = is_array($response)
? ($response['access_token'] ?? $response['token'] ?? null)
: null;
if (!$token) {
Log::error('Montzar login failed', [
'base_url' => $this->baseUrl(),
'mobile' => $this->config()->Config('montzar_mobile'),
'response' => $response,
]);
// 422 تا فرانت toast نشان دهد؛ جزئیات فنی فقط در لاگ می‌ماند
throw new UnprocessableEntityHttpException(
'سرویس موقتا در دسترس نیست، لطفاً با پشتیبانی تماس بگیرید.'
);
}
Setting::updateOrCreate(
['key' => 'montzar_token'],
['value' => $token, 'is_active' => 1, 'can_edit' => 1]
);
return $token;
}
public function getToken(): string
{
$token = trim((string) $this->config()->Config('montzar_token'));
if (!$token) {
$token = $this->montzarLogin();
}
return $token;
}
public function productList(): array
{
$attempts = 3;
$lastError = null;
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
try {
return $this->productListOnce();
} catch (\Throwable $e) {
$lastError = $e;
Log::warning('Montzar productList attempt failed', [
'attempt' => $attempt,
'max' => $attempts,
'base_url' => $this->baseUrl(),
'error' => $e->getMessage(),
]);
if ($attempt < $attempts) {
usleep(250_000 * $attempt);
}
}
}
throw $lastError ?? new \RuntimeException('Montzar product list failed');
}
private function productListOnce(): array
{
$token = $this->getToken();
try {
$response = $this->http(7)
->withToken($token)
->get($this->endpoint('/server/customer/product/list'))
->throw()
->json();
} catch (\Throwable $e) {
// توکن مرده / قطع شبکه — یک بار لاگین مجدد
$token = $this->montzarLogin();
$response = $this->http(7)
->withToken($token)
->get($this->endpoint('/server/customer/product/list'))
->throw()
->json();
}
if ((isset($response['message']) && $response['message'] === 'no authorised') || $response === null) {
$token = $this->montzarLogin();
$response = $this->http(7)
->withToken($token)
->get($this->endpoint('/server/customer/product/list'))
->throw()
->json();
}
if (!is_array($response)) {
throw new \Exception('Montzar product list response is not array');
}
return $response;
}
/**
* @return array<int, array{id: string, name: string, price: int, buy: int, sell: int}>
*/
public function listProductOptions(): array
{
try {
$response = $this->productList();
} catch (\Throwable $e) {
Log::warning('Montzar product options failed', ['error' => $e->getMessage()]);
return [];
}
if (! is_array($response) || (isset($response['message']) && ! array_is_list($response))) {
return [];
}
return collect($response)
->filter(fn ($row) => is_array($row) && array_key_exists('id', $row))
->map(function (array $row) {
$sub = is_array($row['sub_agent'] ?? null) ? $row['sub_agent'] : [];
$buy = (float) ($sub['rate_buy_gram_price'] ?? 0);
$sell = (float) ($sub['rate_sell_gram_price'] ?? 0);
$price = ($buy > 0 && $sell > 0)
? (int) round(($buy + $sell) / 2)
: (int) round(max($buy, $sell));
$id = (int) ($row['id'] ?? 0);
$name = trim((string) (
$row['name_fa']
?? $row['name']
?? $row['title']
?? $row['product_name']
?? ''
));
return [
'id' => (string) $id,
'name' => $name !== '' ? $name : ('محصول #' . $id),
'price' => $price,
'buy' => (int) round($buy),
'sell' => (int) round($sell),
];
})
->filter(fn (array $row) => $row['id'] !== '' && $row['id'] !== '0')
->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE)
->values()
->all();
}
/**
* مانده طلا/ریال اکانت اپ روی نود همکار (دفتر پنل همکار — نه ته‌حساب اپ).
*
* @return list<array{name_eng?: string, name_fa?: string, value?: float|int, unit?: string, isKol?: bool}>
*/
public function fetchColleagueLedgerMandeh(): array
{
$body = $this->requestColleagueLedger('GET', '/server/customer/fin_tahesab/mandeh');
if (!is_array($body)) {
throw new UnprocessableEntityHttpException('پاسخ نامعتبر مانده دفتر همکار');
}
// بعضی نودها آرایهٔ مستقیم برمی‌گردانند؛ بعضی داخل data
if (isset($body[0]) || $body === []) {
return array_values($body);
}
if (isset($body['data']) && is_array($body['data'])) {
return array_values($body['data']);
}
return [$body];
}
/**
* ریز اسناد دفتر اکانت روی نود همکار در بازهٔ تاریخ میلادی Y-m-d.
*
* @return list<array<string, mixed>>
*/
public function fetchColleagueLedgerTable(string $startDate, string $endDate): array
{
$body = $this->requestColleagueLedger('POST', '/server/customer/fin_tahesab/table', [
'startDate' => $startDate,
'endDate' => $endDate,
]);
if (!is_array($body)) {
throw new UnprocessableEntityHttpException('پاسخ نامعتبر ریز دفتر همکار');
}
if (isset($body[0]) || $body === []) {
return array_values($body);
}
if (isset($body['data']) && is_array($body['data'])) {
return array_values($body['data']);
}
if (isset($body['rows']) && is_array($body['rows'])) {
return array_values($body['rows']);
}
return [];
}
/**
* @param array<string, mixed>|null $payload
* @return array<mixed>|null
*/
private function requestColleagueLedger(string $method, string $path, ?array $payload = null): ?array
{
$token = $this->getToken();
$url = $this->endpoint($path);
$httpResponse = $this->sendColleagueLedgerHttp($method, $url, $token, $payload);
$body = $this->decodeMontzarHttpBody($httpResponse);
if (is_array($body) && ($body['message'] ?? null) === 'no authorised') {
$token = $this->montzarLogin();
$httpResponse = $this->sendColleagueLedgerHttp($method, $url, $token, $payload);
$body = $this->decodeMontzarHttpBody($httpResponse);
}
if ($httpResponse->failed() && !is_array($body)) {
Log::warning('Colleague ledger request failed', [
'method' => $method,
'url' => $url,
'status' => $httpResponse->status(),
'raw' => $httpResponse->body(),
]);
throw new UnprocessableEntityHttpException(
$this->formatMontzarErrorMessage(null, $httpResponse)
);
}
if (is_array($body) && !empty($body['message']) && is_string($body['message']) && !isset($body[0])) {
throw new UnprocessableEntityHttpException($body['message']);
}
return $body;
}
private function sendColleagueLedgerHttp(string $method, string $url, string $token, ?array $payload)
{
$pending = $this->http(30)->withToken($token);
if (strtoupper($method) === 'GET') {
return $pending->get($url, $payload ?? []);
}
return $pending->post($url, $payload ?? []);
}
/**
* آیا پاسخ همکار به‌خاطر کهنه بودن مظنه رد شده؟
*/
public static function isQuoteChangedMessage(?string $message): bool
{
if ($message === null || trim($message) === '') {
return false;
}
$normalized = str_replace(
['ي', 'ك', 'ة', '!', '?', '؟', '.'],
['ی', 'ک', 'ه', '', '', '', ''],
$message
);
$normalized = preg_replace('/\s+/u', ' ', $normalized) ?? $normalized;
$normalized = trim($normalized);
if ($normalized === '') {
return false;
}
if (mb_stripos($normalized, 'تغییر در مظنه') !== false) {
return true;
}
if (mb_stripos($normalized, 'مظنه') !== false && mb_stripos($normalized, 'تغییر') !== false) {
return true;
}
$latin = mb_strtolower($normalized);
return str_contains($latin, 'quote')
&& (str_contains($latin, 'change') || str_contains($latin, 'changed'));
}
/**
* ثبت سفارش در منتظر — قبل از ایجاد سفارش/تراکنش محلی صدا زده می‌شود.
*
* @return array{ok: bool, provider_id: int|null, error: string|null}
*/
public function submitOrder(array &$data): array
{
$attempt = $this->submitOrderOnce($data);
if (
!($attempt['parsed']['ok'] ?? false)
&& self::isQuoteChangedMessage($attempt['parsed']['error'] ?? null)
) {
Log::info('Montzar quote changed — refreshing mazaneh and retrying', [
'error' => $attempt['parsed']['error'] ?? null,
'old_mazaneh' => $attempt['payload']['mazaneh'] ?? null,
]);
$refreshed = false;
try {
$refreshed = $this->syncQuoteSettingsFromProvider();
} catch (\Throwable $e) {
Log::warning('Montzar quote refresh failed', [
'error' => $e->getMessage(),
]);
}
if (!$refreshed) {
$refreshed = $this->applyQuoteFromErrorBody(
is_array($attempt['body'] ?? null) ? $attempt['body'] : null,
(int) ($attempt['payload']['mazaneh'] ?? 0)
);
}
if ($refreshed) {
$attempt = $this->submitOrderOnce($data);
}
}
$parsed = $attempt['parsed'];
if (!$parsed['ok']) {
Log::warning('Montzar order rejected', [
'base_url' => $this->baseUrl(),
'status' => $attempt['http']->status(),
'body' => $attempt['body'],
'raw' => $attempt['http']->body(),
'payload' => $attempt['payload'],
'reason' => $parsed['error'],
]);
throw new UnprocessableEntityHttpException(
$this->formatMontzarErrorMessage($parsed['error'], $attempt['http'])
);
}
return [
'ok' => true,
'provider_id' => $parsed['provider_id'] ?? null,
'error' => null,
];
}
public function applyParsedOrder(array &$data, array $parsed): void
{
$this->syncOrderWithMontzarResponse($data, $parsed);
}
public function sendOrder(array &$data)
{
$parsed = $this->submitOrder($data);
$this->applyParsedOrder($data, $parsed);
return null;
}
/**
* @return array{parsed: array, body: mixed, payload: array, http: \Illuminate\Http\Client\Response}
*/
private function submitOrderOnce(array &$data): array
{
$payload = $this->makeOrderPayload($data);
$token = $this->getToken();
$url = $this->endpoint('/server/customer/order');
$httpResponse = $this->postToMontzar($url, $token, $payload);
$body = $this->decodeMontzarHttpBody($httpResponse);
if (is_array($body) && ($body['message'] ?? null) === 'no authorised') {
$token = $this->montzarLogin();
$httpResponse = $this->postToMontzar($url, $token, $payload);
$body = $this->decodeMontzarHttpBody($httpResponse);
}
$this->logMontzarOrderResponse($httpResponse, $body);
return [
'parsed' => $this->parseMontzarOrderResponse($body, (int) $data['direction']),
'body' => $body,
'payload' => $payload,
'http' => $httpResponse,
];
}
/**
* مظنهٔ تازهٔ همکار را از لیست محصول روی settings می‌نویسد.
*/
public function syncQuoteSettingsFromProvider(): bool
{
$products = $this->productList();
if (isset($products['message']) && !array_is_list($products)) {
Log::warning('Montzar quote refresh got message payload', [
'message' => $products['message'],
]);
return false;
}
$productId = (int) ($this->config()->Config('montzar_product_id') ?: 25);
$product = collect($products)->firstWhere('id', $productId);
if (!is_array($product) || !isset($product['sub_agent']) || !is_array($product['sub_agent'])) {
Log::warning('Montzar quote refresh: product not found', [
'product_id' => $productId,
]);
return false;
}
$sub = $product['sub_agent'];
$buyGram = (float) ($sub['rate_buy_gram_price'] ?? 0);
$sellGram = (float) ($sub['rate_sell_gram_price'] ?? 0);
$buyMazaneh = $sub['rate_buy_price'] ?? 0;
$sellMazaneh = $sub['rate_sell_price'] ?? 0;
if ((int) $buyMazaneh <= 0 && (int) $sellMazaneh <= 0) {
return false;
}
$mid = ($buyGram > 0 && $sellGram > 0) ? (($buyGram + $sellGram) / 2) : 0;
$diff = ($buyGram > 0 && $sellGram > 0) ? (($buyGram - $sellGram) / 2) : 0;
if ($mid > 0) {
$this->config()->UpdateConfig('montzar_live_price', (float) $mid);
$this->config()->UpdateConfig('montzar_diff', (float) $diff);
$this->config()->UpdateConfig('montzar_mazaneh', ((float) $mid * 4.331802));
}
if ((int) $buyMazaneh > 0) {
$this->config()->UpdateConfig('montzar_truly_buy_mazaneh', $buyMazaneh);
}
if ((int) $sellMazaneh > 0) {
$this->config()->UpdateConfig('montzar_truly_sell_mazaneh', $sellMazaneh);
}
$this->config()->UpdateConfig(
'montzar_last_product_log',
json_encode($product, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
);
Log::info('Montzar mazaneh refreshed before order retry', [
'buy_mazaneh' => $buyMazaneh,
'sell_mazaneh' => $sellMazaneh,
'live_price' => $mid,
]);
return true;
}
/**
* اگر خودِ خطای همکار مظنهٔ جدید را برگرداند، همان را ذخیره کن.
* مظنهٔ اکو‌شدهٔ همان درخواست قبلی را دوباره ذخیره نکن.
*
* @param array<string, mixed>|null $body
*/
private function applyQuoteFromErrorBody(?array $body, int $previousMazaneh): bool
{
if ($body === null) {
return false;
}
$data = is_array($body['data'] ?? null) ? $body['data'] : $body;
if (!is_array($data)) {
return false;
}
$buyMazaneh = $this->firstPositiveNumber($data, [
'rate_buy_price',
'buy_mazaneh',
'mazaneh_buy',
'truly_buy_mazaneh',
]);
$sellMazaneh = $this->firstPositiveNumber($data, [
'rate_sell_price',
'sell_mazaneh',
'mazaneh_sell',
'truly_sell_mazaneh',
]);
$mazaneh = $this->firstPositiveNumber($data, ['new_mazaneh', 'new_quote']);
$updated = false;
if ($buyMazaneh !== null && $buyMazaneh !== $previousMazaneh) {
$this->config()->UpdateConfig('montzar_truly_buy_mazaneh', $buyMazaneh);
$updated = true;
}
if ($sellMazaneh !== null && $sellMazaneh !== $previousMazaneh) {
$this->config()->UpdateConfig('montzar_truly_sell_mazaneh', $sellMazaneh);
$updated = true;
}
if (!$updated && $mazaneh !== null && $mazaneh !== $previousMazaneh) {
$this->config()->UpdateConfig('montzar_truly_buy_mazaneh', $mazaneh);
$this->config()->UpdateConfig('montzar_truly_sell_mazaneh', $mazaneh);
$updated = true;
}
if ($updated) {
Log::info('Montzar mazaneh taken from error body', [
'buy_mazaneh' => $buyMazaneh,
'sell_mazaneh' => $sellMazaneh,
'mazaneh' => $mazaneh,
]);
}
return $updated;
}
/**
* @param array<string, mixed> $data
* @param list<string> $keys
*/
private function firstPositiveNumber(array $data, array $keys): ?int
{
foreach ($keys as $key) {
if (!isset($data[$key]) || !is_numeric($data[$key])) {
continue;
}
$value = (int) $data[$key];
if ($value > 0) {
return $value;
}
}
return null;
}
private function makeOrderPayload(array $data): array
{
$mazaneh = ((int) $data['direction'] === 1)
? $this->config()->Config('montzar_truly_buy_mazaneh')
: $this->config()->Config('montzar_truly_sell_mazaneh');
$montzarPrice = (float) $this->config()->Config('montzar_live_price');
$cash = (int) round(
$data['total_price'] ?? ($montzarPrice * (float) $data['amount'])
);
$customerName = auth('customer')->check()
? auth('customer')->user()->name
: ($data['customer']['name'] ?? '');
$payload = [
'mazaneh' => (int) $mazaneh,
'amount' => (float) $data['amount'],
'description' => $this->config()->Config('name_fa') . ' - ' . $customerName,
'direction' => (int) $data['direction'],
'product_id' => (int) ($this->config()->Config('montzar_product_id') ?: 25),
'cash' => $cash,
'input_mode' => 'gold',
];
if (
$this->config()->getBool('montzar_order_wait_callback', true)
&& isset($data['order'])
&& $data['order'] instanceof Order
) {
$payload['callback'] = app(ColleagueOrderCallbackService::class)->callbackUrl((int) $data['order']->id);
}
return $payload;
}
/**
* GET وضعیت سفارش روی نود همکار — مسیر از setting + شناسه سفارش همکار.
*
* @return array<string, mixed>|null
*/
public function fetchColleagueOrder(int $providerOrderId): ?array
{
if ($providerOrderId <= 0) {
return null;
}
$path = trim($this->config()->getString('montzar_order_status_path', '/server/customer/order'));
if ($path === '') {
$path = '/server/customer/order';
}
$url = $this->endpoint(rtrim($path, '/') . '/' . $providerOrderId);
$token = $this->getToken();
try {
$httpResponse = $this->http(15)->withToken($token)->get($url);
} catch (\Throwable $e) {
Log::warning('Montzar order status request failed', [
'url' => $url,
'error' => $e->getMessage(),
]);
return null;
}
$body = $this->decodeMontzarHttpBody($httpResponse);
if (is_array($body) && ($body['message'] ?? null) === 'no authorised') {
$token = $this->montzarLogin();
$httpResponse = $this->http(15)->withToken($token)->get($url);
$body = $this->decodeMontzarHttpBody($httpResponse);
}
return is_array($body) ? $body : null;
}
private function postToMontzar(string $url, string $token, array $payload)
{
return $this->http(15)
->withToken($token)
->post($url, $payload);
}
private function decodeMontzarHttpBody($httpResponse): ?array
{
$body = $httpResponse->json();
if (is_array($body)) {
return $body;
}
$raw = trim((string) $httpResponse->body());
if ($raw === '') {
return null;
}
$decoded = json_decode($raw, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
return $decoded;
}
return null;
}
private function logMontzarOrderResponse($httpResponse, $body): void
{
try {
$value = $body === null
? $httpResponse->body()
: json_encode($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// مربوط به منتزر: settings
$this->config()->UpdateConfig('montzar_order_response', $value);
// عمومی: اگر در const/Cnt داری همان‌جا بماند
Cnt::where('key', 'last_order_log')->update(['value' => $value]);
} catch (\Exception $e) {
Log::warning('Could not persist montzar order response', [
'error' => $e->getMessage(),
]);
}
}
private function formatMontzarErrorMessage(?string $error, $httpResponse): string
{
if ($error && $error !== 'پاسخ نامعتبر از سرور منتظر (اتصال یا فرمت JSON)') {
return $error;
}
$status = $httpResponse->status();
$raw = trim((string) $httpResponse->body());
if ($raw === '') {
return "خطا در ارتباط با منتظر (HTTP {$status})";
}
if (stripos($raw, '<!DOCTYPE') !== false || stripos($raw, '<html') !== false) {
return "خطا از سرویس منتظر (HTTP {$status}). پاسخ JSON دریافت نشد.";
}
return mb_substr($raw, 0, 300);
}
private function parseMontzarOrderResponse($body, int $direction): array
{
if (!is_array($body)) {
return [
'ok' => false,
'provider_id' => null,
'error' => 'پاسخ نامعتبر از سرور منتظر (اتصال یا فرمت JSON)',
];
}
if (!empty($body['message']) && is_string($body['message'])) {
return [
'ok' => false,
'provider_id' => null,
'error' => $body['message'],
];
}
$providerId = $this->extractProviderOrderId($body);
if ($providerId !== null) {
return [
'ok' => true,
'provider_id' => $providerId,
'error' => null,
];
}
$sideKey = $direction === 1 ? 'buy' : 'sell';
if (($body[$sideKey] ?? null) === true) {
return [
'ok' => true,
'provider_id' => null,
'error' => null,
];
}
foreach ($body as $item) {
if (!is_array($item)) {
continue;
}
if (!empty($item['id']) && is_numeric($item['id'])) {
return [
'ok' => true,
'provider_id' => (int) $item['id'],
'error' => null,
];
}
if (!empty($item['order']['id'])) {
return [
'ok' => true,
'provider_id' => (int) $item['order']['id'],
'error' => null,
];
}
if (isset($item['sts']) && in_array((int) $item['sts'], [1, 2, 4], true)) {
return [
'ok' => true,
'provider_id' => !empty($item['id']) ? (int) $item['id'] : null,
'error' => null,
];
}
}
if (!empty($body['pending']) || !empty($body[0]['pending'])) {
return [
'ok' => true,
'provider_id' => $this->extractProviderOrderId($body),
'error' => null,
];
}
return [
'ok' => false,
'provider_id' => null,
'error' => 'سفارشات توسط ارائه دهنده بسته شده است.',
];
}
private function extractProviderOrderId(array $body): ?int
{
if (!empty($body['id'])) {
return (int) $body['id'];
}
if (!empty($body['order_id']) && is_numeric($body['order_id'])) {
return (int) $body['order_id'];
}
if (!empty($body['order']['id'])) {
return (int) $body['order']['id'];
}
foreach ([0, 1, 2] as $index) {
if (isset($body[$index]) && is_array($body[$index])) {
if (!empty($body[$index]['id'])) {
return (int) $body[$index]['id'];
}
if (!empty($body[$index]['order_id']) && is_numeric($body[$index]['order_id'])) {
return (int) $body[$index]['order_id'];
}
if (!empty($body[$index]['order']['id'])) {
return (int) $body[$index]['order']['id'];
}
}
}
foreach (['buy', 'sell'] as $sideKey) {
if (!isset($body[$sideKey]) || !is_array($body[$sideKey])) {
continue;
}
if (!empty($body[$sideKey]['id'])) {
return (int) $body[$sideKey]['id'];
}
if (!empty($body[$sideKey]['order']['id'])) {
return (int) $body[$sideKey]['order']['id'];
}
}
return null;
}
private function syncOrderWithMontzarResponse(array &$data, array $parsed): void
{
$montzarDiff = (float) $this->config()->Config('montzar_diff');
$montzarPrice = (float) $this->config()->Config('montzar_live_price');
$totalPriceMontzar = ((int) $data['direction'] === 1)
? ($montzarPrice + $montzarDiff)
: ($montzarPrice - $montzarDiff);
$providerOrderId = $parsed['provider_id'] ?? null;
if (isset($data['order']) && $data['order'] instanceof Order) {
if ($providerOrderId !== null && Schema::hasColumn('orders', 'provider_id')) {
$data['order']->provider_id = $providerOrderId;
}
if (Schema::hasColumn('orders', 'total_price_montzar')) {
$data['order']->total_price_montzar = $totalPriceMontzar;
}
$data['order']->save();
}
$data['total_price_montzar'] = $totalPriceMontzar;
if ($providerOrderId !== null) {
$data['provider_id'] = $providerOrderId;
}
}
}