Files
clinicpro/docs/tasks/task-15-payment/implementation_notes.md
T
hamed de1a78a235 feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
2026-06-09 22:00:34 +03:30

3.0 KiB
Raw Blame History

نکات پیاده‌سازی — تسک ۱۵: ماژول پرداخت

درگاه‌های واقعی پروژه (از کد Drupal)

۱. بانک ملت (Mellat) — پروتکل SOAP

// وب‌سرویس SOAP با متدهای:
// bpPayRequest      → شروع تراکنش
// bpVerifyRequest   → تأیید پرداخت
// bpInquiryRequest  → استعلام وضعیت
// bpSettleRequest   → تسویه
// bpReversalRequest → برگشت تراکنش

// پارامترهای پیکربندی:
terminal_id, username, password
wsdl_endpoint, gate_url
test_mode (boolean)
callback_url, callback_url_test

۲. SEP (سامان) — درگاه دوم

در sep_payment/src/Plugin/MyPayment/SepPayment.php پیاده‌سازی شده.

پیاده‌سازی در Symfony

interface PaymentGatewayInterface {
    public function pay(array $data): array;    // { success, ref_id, gateway_url }
    public function verify(array $callbackData, int $orderId): array;
    public function refund(array $data): array;
}

پیکربندی در .env:

PAYMENT_GATEWAY=mellat   # mellat | sep
MELLAT_TERMINAL_ID=...
MELLAT_USERNAME=...
MELLAT_PASSWORD=...
MELLAT_TEST_MODE=true

وضعیت پرداخت (از کد واقعی)

pending   → بعد از ایجاد پرداخت
received  → بعد از تأیید موفق (نه "paid"!)
failed    → پرداخت ناموفق

⚠️ در Drupal status موفق received است نه paid.

شرط ایجاد پرداخت

appointment باید status=waiting_for_payment داشته باشد. اگر status متفاوت باشد → 400 error.

قیمت از Config (نه Request)

// مبلغ از پیکربندی خوانده می‌شود، نه از request body!
$amount = $config->get('payment.settings')['price'];

→ در .env یا config:

APPOINTMENT_PRICE=500000

frontend_address

پرداخت دارای frontend_address است — URL فرانت برای redirect بعد از پرداخت. این به representation مرتبط است (سیستم multi-tenant).

Callback Mellat

POST /payment/callback/mellat
  RefId=...
  ResCode=0
  SaleOrderId=...
  SaleReferenceId=...

جریان:
1. ResCode === '0' باشد
2. bpVerifyRequest → اگر موفق نبود → bpInquiryRequest → اگر موفق نبود → bpReversalRequest
3. bpSettleRequest (resCode='0' یا '45' = قبلاً تسویه شده)
4. appointment.status = confirmed
5. payment.status = received
6. payment.ref_id = SaleReferenceId

Idempotency

اگر callback دوبار بیاید، دوبار process نشود:

if ($payment->getStatus() === 'received') {
    return; // قبلاً پردازش شده
}

مجوزها

POST   /payment              → احراز هویت‌شده
GET    /payment/{uuid}       → owner یا ROLE_ADMIN یا دکتر مرتبط
GET    /my-payments          → owner
GET/POST callback            → عمومی (درگاه پرداخت)