Passed
Push — master ( edd315...5c8f6b )
by Adrien
07:14
created

DatatransAction::createMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 3
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Action;
6
7
use Application\Model\Account;
8
use Application\Model\Transaction;
9
use Application\Model\TransactionLine;
10
use Application\Model\User;
11
use Cake\Chronos\Date;
12
use Doctrine\ORM\EntityManager;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\ServerRequestInterface;
15
use Psr\Http\Server\RequestHandlerInterface;
16
use Zend\Diactoros\Response\HtmlResponse;
17
use Zend\Expressive\Template\TemplateRendererInterface;
18
19
class DatatransAction extends AbstractAction
20
{
21
    /** @var TemplateRendererInterface */
22
    private $template;
23
24
    /**
25
     * @var EntityManager
26
     */
27
    private $entityManager;
28
29 9
    public function __construct(EntityManager $entityManager, TemplateRendererInterface $template)
30
    {
31 9
        $this->entityManager = $entityManager;
32 9
        $this->template = $template;
33 9
    }
34
35
    /**
36
     * Webhook called by datatrans when a payment was made
37
     *
38
     * See documentation: https://api-reference.datatrans.ch/#failed-unsuccessful-authorization-response
39
     *
40
     * @param ServerRequestInterface $request
41
     * @param RequestHandlerInterface $handler
42
     *
43
     * @return ResponseInterface
44
     */
45 9
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
46
    {
47 9
        $request->getMethod();
48 9
        $body = $request->getParsedBody();
49
50
        try {
51 9
            if (!is_array($body)) {
52 1
                throw new \Exception('Parsed body is expected to be an array but got: ' . gettype($body));
53
            }
54
55 8
            $status = $body['status'] ?? '';
56
57 8
            $message = $this->dispatch($status, $body);
58 5
        } catch (\Throwable $exception) {
59 5
            $message = $this->createMessage('error', $exception->getMessage(), is_array($body) ? $body : []);
60
        }
61
62
        $viewModel = [
63 9
            'message' => $message,
64
        ];
65
66 9
        return new HtmlResponse($this->template->render('app::datatrans', $viewModel));
67
    }
68
69
    /**
70
     * Create a message in a coherent way
71
     *
72
     * @param string $status
73
     * @param string $message
74
     * @param array $detail
75
     *
76
     * @return array
77
     */
78 9
    private function createMessage(string $status, string $message, array $detail): array
79
    {
80
        return [
81 9
            'status' => $status,
82 9
            'message' => $message,
83 9
            'detail' => $detail,
84
        ];
85
    }
86
87
    /**
88
     * Dispatch the data received from Datatrans to take appropriate actions
89
     *
90
     * @param string $status
91
     * @param $body
92
     *
93
     * @return array
94
     */
95 8
    private function dispatch(string $status, array $body): array
96
    {
97 8
        switch ($status) {
98 8
            case 'success':
99 5
                $this->createTransactions($body);
100 2
                $message = $this->createMessage($status, $body['responseMessage'], $body);
101
102 2
                break;
103 3
            case 'error':
104 1
                $message = $this->createMessage($status, $body['errorMessage'], $body);
105
106 1
                break;
107 2
            case 'cancel':
108 1
                $message = $this->createMessage($status, 'Cancelled', $body);
109
110 1
                break;
111
            default:
112 1
                throw new \Exception('Unsupported status in Datatrans data: ' . $status);
113
        }
114
115 4
        return $message;
116
    }
117
118 5
    private function createTransactions(array $body): void
119
    {
120 5
        $userId = $body['refno'] ?? null;
121
122
        /** @var User $user */
123 5
        $user = $this->entityManager->getRepository(User::class)->getOneById((int) $userId);
124 5
        if (!$user) {
125 1
            throw new \Exception('Cannot create transactions without a user');
126
        }
127
128 4
        $account = $this->entityManager->getRepository(Account::class)->getOrCreate($user);
129
130 4
        if (!array_key_exists('amount', $body)) {
131
            // Do not support "registrations"
132 1
            throw new \Exception('Cannot create transactions without an amount');
133
        }
134
135 3
        $currency = $body['currency'] ?? '';
136 3
        if ($currency !== 'CHF') {
137 1
            throw new \Exception('Can only create transactions for CHF, but got: ' . $currency);
138
        }
139
140 2
        $now = Date::today();
141 2
        $datatransRef = $body['uppTransactionId'];
142 2
        $name = 'Datatrans: ' . $datatransRef;
143
144 2
        $transaction = new Transaction();
145 2
        $this->entityManager->persist($transaction);
146 2
        $transaction->setName($name);
147 2
        $transaction->setTransactionDate($now);
148
149 2
        $line = new TransactionLine();
150 2
        $this->entityManager->persist($line);
151 2
        $line->setName($name);
152 2
        $line->setTransactionDate($now);
153 2
        $line->setBalance((string) ($body['amount'] / 100));
154 2
        $line->setDatatransRef($datatransRef);
155 2
        $line->setTransaction($transaction);
156 2
        $line->setCredit($account);
157
158
        // This could be removed later on. For now it's mostly for debugging
159 2
        $line->setRemarks(json_encode($body, JSON_PRETTY_PRINT));
160
161 2
        $this->entityManager->flush();
162 2
    }
163
}
164