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'); } }