Files
clinicpro/.claude/prompt/fix-prod-soap-extension.md
T

115 lines
6.1 KiB
Markdown
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.
# رفع خطای `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 (خطوط ~6273):
```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 تغییر کند لازم می‌شد).