ldc-boutique-cnc/app/Console/Commands/SystempayCreditTransactionUpdateCommand.php
2023-05-19 12:11:38 +02:00

254 lines
7.7 KiB
PHP

<?php
namespace App\Jobs\Payment;
use App\Enums\Order\PaymentModeEnum;
use App\Enums\Order\TransactionStatus;
use App\Enums\Order\TransactionType;
use App\Models\Payment\Transaction;
use LDC\LaravelSystempay\Enums\ResponseType;
use LDC\LaravelSystempay\LaravelSystempay;
/**
* Ping transaction status and update itself.
* Utilise du code de laravel-systempay pour parser le statut de la réponse
* Ceci est un doublon car Systempay ne notifie pas de transactions remboursés depuis la boutique
*/
class SystempayCreditTransactionUpdate
{
/**
* Used in loop
*
* @var \App\Models\Payment\Transaction|null
*/
private $currentTransaction;
/**
* Used in loop
*
* @var \Illuminate\Support\Collection|null
*/
private $currentTransactionResponse;
/**
* Called when the job will be executed.
*
* @return void
*/
public function handle()
{
Transaction::query()
->where('type', TransactionType::credit())
->where('status', TransactionStatus::pending())
->where('payment_mode', PaymentModeEnum::systemPay())
->each(function (Transaction $transaction) {
$this->currentTransaction = $transaction;
\call_user_func([$this, 'handleTransaction']);
});
}
/**
* Fetch system pay transaction and update its status
*
* @return void
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
private function handleTransaction(): void
{
// phpcs:enable
$systemPay = $this->getPayment();
$client = $systemPay->generateRestClient();
$this->currentTransactionResponse = $client->get(
$this->currentTransaction->foreign_refund_id
);
$reponseType = $this->getReponseType(data_get($this->currentTransactionResponse, 'answer.detailedStatus'));
switch ($reponseType) {
case ResponseType::acceptedPayment():
$this->currentTransaction->status = TransactionStatus::validated();
$this->currentTransaction->saveOrFail();
$this->currentTransaction->mutateOrderStatus();
break;
case ResponseType::expiredPayment():
case ResponseType::canceledPayment():
case ResponseType::refusedPayment():
$this->currentTransaction->status = TransactionStatus::failed();
$this->currentTransaction->saveOrFail();
$this->currentTransaction->mutateOrderStatus();
break;
// ! Ignore these explicitely.
case ResponseType::suspendedPayment():
case ResponseType::pendingPayment():
break;
case ResponseType::needsValidation():
case ResponseType::suspectedFraud():
case ResponseType::unknown():
default:
$message = __CLASS__ . " Unhandled Systempay credit reponse Type '{$reponseType}'";
\report(new \RuntimeException($message));
} //end switch
}
/**
* Fetch the appropriate reponse type
*
* @param string $transStatus
* @return \LDC\LaravelSystempay\Enums\ResponseType
*/
private function getReponseType(string $transStatus): ResponseType
{
switch (true) {
case $this->isAcceptedPayment($transStatus):
return ResponseType::acceptedPayment();
case \in_array($transStatus, ['REFUSED']):
return ResponseType::refusedPayment();
case \in_array($transStatus, ['EXPIRED']):
return ResponseType::expiredPayment();
case \in_array($transStatus, ['SUSPENDED']):
return ResponseType::suspendedPayment();
case $this->isCanceledPayment($transStatus) or
\in_array($transStatus, ['CANCELLED']):
return ResponseType::canceledPayment();
case $this->isPendingPayment($transStatus):
return ResponseType::pendingPayment();
case $this->isToValidatePayment($transStatus):
return ResponseType::needsValidation();
case $this->isSuspectedFraud($transStatus):
return ResponseType::suspectedFraud();
default:
return ResponseType::unknown();
} //end switch
}
/**
* Check if the payment was successful (waiting confirmation or captured).
*
* @param string $transStatus
* @return boolean
*/
private function isAcceptedPayment(string $transStatus): bool
{
$confirmed_statuses = [
'AUTHORISED',
'AUTHORISED_TO_VALIDATE',
'CAPTURED',
/** capture will be redone */
'CAPTURE_FAILED'
];
return in_array($transStatus, $confirmed_statuses) || $this->isPendingPayment($transStatus);
}
/**
* Check if the payment is waiting confirmation (successful but the amount has not been
* transfered and is not yet guaranteed).
*
* @param string $transStatus
* @return boolean
*/
private function isPendingPayment(string $transStatus): bool
{
$pending_statuses = [
'INITIAL',
'WAITING_AUTHORISATION',
'WAITING_AUTHORISATION_TO_VALIDATE',
'UNDER_VERIFICATION'
];
return in_array($transStatus, $pending_statuses);
}
/**
* Check if the payment process was interrupted by the client.
*
* @param string $transStatus
* @return boolean
*/
private function isCanceledPayment(string $transStatus): bool
{
$canceled_statuses = [
'NOT_CREATED',
'ABANDONED'
];
return in_array($transStatus, $canceled_statuses);
}
/**
* Check if the payment is to validate manually in the payment platform Back Office.
*
* @param string $transStatus
* @return boolean
*/
private function isToValidatePayment(string $transStatus): bool
{
$to_validate_statuses = [
'WAITING_AUTHORISATION_TO_VALIDATE',
'AUTHORISED_TO_VALIDATE'
];
return in_array($transStatus, $to_validate_statuses);
}
/**
* Check if the payment is suspected to be fraudulent.
*
* @param string $transStatus
* @return boolean
*/
private function isSuspectedFraud(string $transStatus): bool
{
// At least one control failed ...
$risk_control = $this->getRiskControl($transStatus);
if (in_array('WARNING', $risk_control) || in_array('ERROR', $risk_control)) {
return true;
}
// Or there was an alert from risk assessment module.
$risk_assessment = $this->getRiskAssessment($transStatus);
if (in_array('INFORM', $risk_assessment)) {
return true;
}
return false;
}
/**
* Return the risk control result.
*
* @return array[string][string]
*/
public function getRiskControl()
{
return data_get(
$this->currentTransactionResponse,
'answer.transactionDetails.fraudManagement.riskControl'
) ?? [];
}
/**
* Return the risk assessment result.
*
* @return array[string]
*/
public function getRiskAssessment()
{
return data_get(
$this->currentTransactionResponse,
'answer.transactionDetails.fraudManagement.riskAnalysis'
) ?? [];
}
/**
* Get Systempay adapter
*
* @return \LDC\LaravelSystempay\LaravelSystempay
*/
private function getPayment(): LaravelSystempay
{
return app('systempay');
}
}