feat(payment): implement PaymentManager for handling payment logic and callbacks
- Refactor PaymentController to delegate payment processing to PaymentManager. - Add findByOrderIdForUpdate method in PaymentRepository for pessimistic locking. - Create PaymentLog entity and repository for auditing payment actions. - Implement startGatewayHandoff and processCallback methods in PaymentManager. - Introduce transaction handling and logging for payment verification. - Update payment flow to ensure idempotency and prevent race conditions. - Enhance security by logging sensitive actions without exposing credentials. - Update database schema with migration for payment_logs table. - Document changes in payment flow architecture.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Payment\Entity;
|
||||
|
||||
use App\Payment\Repository\PaymentLogRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* ردپای حسابرسی (audit trail) هر گام از چرخهٔ پرداخت.
|
||||
* اعتبارنامهٔ درگاه هرگز اینجا ذخیره نمیشود.
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: PaymentLogRepository::class)]
|
||||
#[ORM\Table(name: 'payment_logs')]
|
||||
#[ORM\Index(columns: ['payment_id'], name: 'idx_payment_logs_payment')]
|
||||
class PaymentLog
|
||||
{
|
||||
public const ACTION_INITIATE = 'initiate';
|
||||
public const ACTION_VERIFY = 'verify';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(name: 'payment_id', type: 'integer')]
|
||||
private int $paymentId;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 20)]
|
||||
private string $action;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 20)]
|
||||
private string $gateway;
|
||||
|
||||
/** نتیجهٔ گام: success | failed | canceled | pending */
|
||||
#[ORM\Column(type: 'string', length: 20)]
|
||||
private string $result;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
private ?string $authority = null;
|
||||
|
||||
#[ORM\Column(name: 'client_ip', type: 'string', length: 45, nullable: true)]
|
||||
private ?string $clientIp = null;
|
||||
|
||||
#[ORM\Column(type: 'json', nullable: true)]
|
||||
private ?array $payload = null;
|
||||
|
||||
#[ORM\Column(name: 'created_at', type: 'integer')]
|
||||
private int $createdAt;
|
||||
|
||||
public function __construct(
|
||||
int $paymentId,
|
||||
string $action,
|
||||
string $gateway,
|
||||
string $result,
|
||||
?string $authority = null,
|
||||
?string $clientIp = null,
|
||||
?array $payload = null,
|
||||
) {
|
||||
$this->paymentId = $paymentId;
|
||||
$this->action = $action;
|
||||
$this->gateway = $gateway;
|
||||
$this->result = $result;
|
||||
$this->authority = $authority;
|
||||
$this->clientIp = $clientIp;
|
||||
$this->payload = $payload;
|
||||
$this->createdAt = time();
|
||||
}
|
||||
|
||||
public function getId(): ?int { return $this->id; }
|
||||
public function getPaymentId(): int { return $this->paymentId; }
|
||||
public function getAction(): string { return $this->action; }
|
||||
public function getResult(): string { return $this->result; }
|
||||
}
|
||||
Reference in New Issue
Block a user