115 lines
6.1 KiB
Markdown
115 lines
6.1 KiB
Markdown
# رفع خطای `Class "SoapClient" not found` در پرداخت ملت (سرور prod)
|
||
|
||
## پروژه
|
||
|
||
`clinicpro` (backend / infra — Docker image)
|
||
|
||
## زمینه
|
||
|
||
درگاه ملت (`MellatGateway`) در مسیر prod برای فراخوانی وبسرویس بانک از `new \SoapClient(...)` استفاده میکند (کلاس PHP از افزونه `ext-soap`). روی محیط لوکال (ddev) افزونه `soap` بهصورت پیشفرض نصب است، پس مشکلی دیده نمیشود؛ اما image پرود بر پایه `php:8.2-fpm-alpine` ساخته میشود و در `Dockerfile` فقط این افزونهها نصب میشوند:
|
||
|
||
```dockerfile
|
||
docker-php-ext-install pdo_mysql intl opcache
|
||
```
|
||
|
||
`soap` نصب نمیشود، بنابراین در پرود هنگام initiate کردن پرداخت، fatal میشود:
|
||
|
||
```
|
||
Payment initiate failed (mellat): Class "SoapClient" not found @ /app/src/Payment/Gateway/MellatGateway.php:70
|
||
context {"orderId":"6","amount":150000}
|
||
```
|
||
|
||
## مشکل / هدف
|
||
|
||
افزونه `ext-soap` روی image پرود نصب شود تا مسیر prod درگاه ملت (`bpPayRequest` / `bpVerifyRequest` / `bpSettleRequest` / `bpRefundRequest` / `bpReversalRequest`) روی `SoapClient` کار کند.
|
||
|
||
نکته مهم: sandbox این مشکل را ندارد چون مسیر sandbox از REST (JSON + Basic Auth) استفاده میکند نه SOAP. مشکل فقط در حالت prod (فلگ `mellat_sandbox` غیرفعال) رخ میدهد.
|
||
|
||
## فایلهای مرتبط
|
||
|
||
| فایل | نقش |
|
||
|------|-----|
|
||
| `Dockerfile` (stage 3 — runtime) | نصب افزونههای PHP؛ باید `soap` اضافه شود |
|
||
| `src/Payment/Gateway/MellatGateway.php` | خط ۷۰: `new \SoapClient($this->wsdlUrl(), ...)` — نیازمند `ext-soap` |
|
||
| `composer.json` | افزودن `ext-soap` به `require` تا نبود افزونه زودتر (build-time) شناسایی شود |
|
||
|
||
## وضعیت فعلی
|
||
|
||
`Dockerfile` — بلوک نصب افزونهها در stage runtime (خطوط ~62–73):
|
||
|
||
```dockerfile
|
||
RUN set -eu; \
|
||
export MAKEFLAGS="-j$(nproc)"; \
|
||
retry() { for i in 1 2 3 4 5; do "$@" && return 0; echo "retry $i: $*"; sleep 5; done; return 1; }; \
|
||
retry apk add --no-cache nginx supervisor icu-libs \
|
||
&& retry apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev \
|
||
&& docker-php-ext-install pdo_mysql intl opcache \
|
||
&& retry curl -sSLf "https://github.com/phpredis/phpredis/archive/refs/tags/${PHPREDIS_VERSION}.tar.gz" -o /tmp/phpredis.tar.gz \
|
||
&& mkdir -p /usr/src/php/ext/redis \
|
||
&& tar -xf /tmp/phpredis.tar.gz -C /usr/src/php/ext/redis --strip-components=1 \
|
||
&& docker-php-ext-install redis \
|
||
&& rm -rf /tmp/phpredis.tar.gz \
|
||
&& apk del .build-deps
|
||
```
|
||
|
||
`MellatGateway.php:67-78`:
|
||
|
||
```php
|
||
private function soap(): \SoapClient
|
||
{
|
||
if ($this->soap === null) {
|
||
$this->soap = new \SoapClient($this->wsdlUrl(), [
|
||
'trace' => true,
|
||
'exceptions' => true,
|
||
'encoding' => 'UTF-8',
|
||
'connection_timeout' => 10,
|
||
]);
|
||
}
|
||
return $this->soap;
|
||
}
|
||
```
|
||
|
||
## وظایف
|
||
|
||
### ۱. افزودن `soap` به `docker-php-ext-install` در `Dockerfile`
|
||
|
||
افزونهٔ `soap` جزو افزونههای bundled خودِ PHP است و نیاز به شبکه (pecl/github) ندارد؛ فقط برای کامپایل به هدرهای `libxml2-dev` نیاز دارد. `libxml2-dev` را به `.build-deps` اضافه کن (تا بعد از build با `apk del .build-deps` پاک شود) و `soap` را به لیست `docker-php-ext-install` اضافه کن.
|
||
|
||
نتیجهٔ مطلوب:
|
||
|
||
```dockerfile
|
||
&& retry apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev libxml2-dev \
|
||
&& docker-php-ext-install pdo_mysql intl opcache soap \
|
||
```
|
||
|
||
نکته: `libxml2` (کتابخانهٔ runtime، بدون `-dev`) در image پایهٔ PHP موجود است، پس افزونهٔ کامپایلشده در runtime بدون مشکل load میشود و نیازی به افزودن آن به لیست runtime نیست. فقط هدرهای `-dev` که در build لازماند در `.build-deps` قرار میگیرند.
|
||
|
||
### ۲. اعلام نیازمندی در `composer.json`
|
||
|
||
`ext-soap` را به بخش `require` اضافه کن تا اگر روی محیطی افزونه نبود، در زمان `composer install` هشدار/خطا داده شود و مشکل زودتر از runtime دیده شود:
|
||
|
||
```jsonc
|
||
"require": {
|
||
// ... موجود
|
||
"ext-soap": "*"
|
||
}
|
||
```
|
||
|
||
توجه: در `Dockerfile` مرحلهٔ vendor از `--ignore-platform-reqs` استفاده میکند، پس این افزوده build را نمیشکند؛ صرفاً بهعنوان مستندسازی نیازمندی و اعتبارسنجی در محیطهای دیگر عمل میکند.
|
||
|
||
### ۳. تأیید و rebuild
|
||
|
||
بعد از تغییر `Dockerfile`، image پرود باید دوباره build/deploy شود (Coolify). برای تأیید نصب افزونه پس از build:
|
||
|
||
```bash
|
||
php -m | grep -i soap # باید soap را چاپ کند
|
||
php -r 'var_dump(class_exists("SoapClient"));' # bool(true)
|
||
```
|
||
|
||
## نکات مهم
|
||
|
||
- مشکل صرفاً محیطی (نبود افزونه در image پرود) است، نه باگ منطقی در کد `MellatGateway`. کد درست است؛ فقط وابستگی runtime روی پرود غایب بود.
|
||
- بعد از deploy، پرداخت واقعی ملت (حالت prod، فلگ `mellat_sandbox=0`) باید یکبار تست شود تا مسیر SOAP (`bpPayRequest`) واقعاً پاسخ بگیرد.
|
||
- الگوی موجود در `Dockerfile` را رعایت کن: build-deps مجازی که بعد از کامپایل با `apk del .build-deps` حذف میشوند تا حجم image کم بماند. `libxml2-dev` را داخل همین گروه بگذار، نه بهصورت دائمی.
|
||
- این تغییر API را عوض نمیکند؛ نیازی به بهروزرسانی `docs/api/payment.md` نیست (فقط اگر رفتار/قرارداد endpoint تغییر کند لازم میشد).
|