Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
11 | class Api |
||
12 | { |
||
13 | /** |
||
14 | * @var \Payum\Core\HttpClientInterface |
||
15 | */ |
||
16 | protected $client; |
||
17 | |||
18 | /** |
||
19 | * @var \Http\Message\MessageFactory |
||
20 | */ |
||
21 | protected $messageFactory; |
||
22 | |||
23 | /** |
||
24 | * @var array |
||
25 | */ |
||
26 | protected $options = []; |
||
27 | |||
28 | /** |
||
29 | * $encrypter. |
||
30 | * |
||
31 | * @var Encrypter |
||
32 | */ |
||
33 | protected $encrypter; |
||
34 | |||
35 | /** |
||
36 | * @param array $options |
||
37 | * @param \Payum\Core\HttpClientInterface $client |
||
38 | * @param \Http\Message\MessageFactory $messageFactory |
||
39 | * @param Encrypter $encrypter |
||
40 | * |
||
41 | * @throws \Payum\Core\Exception\InvalidArgumentException if an option is invalid |
||
42 | */ |
||
43 | 14 | public function __construct(array $options, HttpClientInterface $client, MessageFactory $messageFactory, Encrypter $encrypter = null) |
|
51 | |||
52 | /** |
||
53 | * parseResponse. |
||
54 | * |
||
55 | * @param array $response |
||
56 | 3 | * @return array |
|
57 | */ |
||
58 | 3 | public function parseResponse($response) |
|
59 | 3 | { |
|
60 | 3 | if (is_string($response) === true) { |
|
61 | $response = $this->parseStr($response); |
||
62 | 3 | } |
|
63 | |||
64 | 3 | if (empty($response['DATA']) === true) { |
|
65 | throw new LogicException('Response content is not valid'); |
||
66 | } |
||
67 | |||
68 | 3 | $data = json_decode($response['DATA'], true); |
|
69 | 3 | ||
70 | 3 | if (json_last_error() !== JSON_ERROR_NONE) { |
|
71 | $data = []; |
||
72 | parse_str(str_replace(',', '&', $response['DATA']), $data); |
||
73 | } |
||
74 | |||
75 | if (isset($data['returnCode']) === true) { |
||
76 | $response['returnCode'] = $data['returnCode']; |
||
77 | } |
||
78 | |||
79 | 9 | if (isset($data['version']) === true) { |
|
80 | $response['version'] = $data['version']; |
||
81 | 9 | } |
|
82 | 9 | ||
83 | 9 | if (isset($data['txnData']) === true) { |
|
84 | $response = array_merge($response, $data['txnData']); |
||
85 | 9 | unset($data['txnData']); |
|
86 | } |
||
87 | |||
88 | return array_merge($response, $data); |
||
89 | 9 | } |
|
90 | |||
91 | 9 | /** |
|
92 | 7 | * getApiEndpoint. |
|
93 | 7 | * |
|
94 | 7 | * @return string |
|
95 | */ |
||
96 | 9 | public function getApiEndpoint($type = 'capture') |
|
97 | 2 | { |
|
98 | 2 | if ($this->options['sandbox'] === false) { |
|
99 | $urls = [ |
||
100 | 9 | 'capture' => $this->isMobile() === false ? 'https://acq.esunbank.com.tw/ACQTrans/esuncard/txnf014s' : 'https://acq.esunbank.com.tw/ACQTrans/esuncard/txnf014m', |
|
101 | 2 | 'cancel' => 'https://acq.esunbank.com.tw/ACQTrans/esuncard/txnf0150', |
|
102 | 2 | 'refund' => 'https://acq.esunbank.com.tw/ACQTrans/esuncard/txnf0160', |
|
103 | 'sync' => 'https://acq.esunbank.com.tw/ACQQuery/esuncard/txnf0180', |
||
104 | 9 | ]; |
|
105 | 2 | } else { |
|
106 | 2 | $urls = [ |
|
107 | 2 | 'capture' => $this->isMobile() === false ? 'https://acqtest.esunbank.com.tw/ACQTrans/esuncard/txnf014s' : 'https://acqtest.esunbank.com.tw/ACQTrans/esuncard/txnf014m', |
|
108 | 'cancel' => 'https://acqtest.esunbank.com.tw/ACQTrans/esuncard/txnf0150', |
||
109 | 9 | 'refund' => 'https://acqtest.esunbank.com.tw/ACQTrans/esuncard/txnf0160', |
|
110 | 'sync' => 'https://acqtest.esunbank.com.tw/ACQQuery/esuncard/txnf0180', |
||
111 | ]; |
||
112 | } |
||
113 | |||
114 | return $urls[$type]; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | 9 | * createTransaction. |
|
119 | * |
||
120 | 9 | * @param array $params |
|
121 | 9 | * @return array |
|
122 | */ |
||
123 | 9 | public function createTransaction(array $params) |
|
124 | { |
||
125 | $supportedParams = [ |
||
126 | // 訂單編號, 由特約商店產生,不可重複,不可 包含【_】字元,英數限用大寫 |
||
127 | 'ONO' => '', |
||
128 | // 回覆位址, 'https://acqtest.esunbank.com.tw/ACQTrans/test/print.jsp' |
||
129 | 'U' => 'https://acqtest.esunbank.com.tw/ACQTrans/test/print.jsp', |
||
130 | // 特店代碼 |
||
131 | 3 | 'MID' => $this->options['MID'], |
|
132 | // 銀行紅利折抵, Y:使用銀行紅利交易。 N:不使用銀行紅利交易 |
||
133 | 3 | 'BPF' => 'N', |
|
134 | // 分期代碼, 三期:0100103 六期:0100106 正式環境參數由業務經辦提供 |
||
135 | 'IC' => '', |
||
136 | // 交易金額, 台幣(901) |
||
137 | 'TA' => '', |
||
138 | // 終端機代號, EC000001(一般交易) EC000002(分期) |
||
139 | 'TID' => 'EC000001', |
||
140 | ]; |
||
141 | |||
142 | 3 | $params = array_replace( |
|
143 | 3 | $supportedParams, |
|
144 | 3 | array_intersect_key($params, $supportedParams) |
|
145 | 3 | ); |
|
146 | 3 | ||
147 | if (empty($params['IC']) === true) { |
||
148 | unset($params['IC']); |
||
149 | 3 | } else { |
|
150 | $params['TID'] = 'EC000002'; |
||
151 | } |
||
152 | |||
153 | $params['BPF'] = strtoupper($params['BPF']); |
||
154 | if ($params['BPF'] === 'N') { |
||
155 | unset($params['BPF']); |
||
156 | } |
||
157 | |||
158 | 4 | return $this->encrypter->encryptRequest($params); |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | 4 | * getTransactionData. |
|
163 | * |
||
164 | 4 | * @param mixed $params |
|
165 | * @return array |
||
166 | 4 | */ |
|
167 | View Code Duplication | public function getTransactionData(array $params) |
|
|
|||
168 | 4 | { |
|
169 | $supportedParams = [ |
||
170 | 4 | // 訂單編號, 由特約商店產生,不可重複,不可 包含【_】字元,英數限用大寫 |
|
171 | 'ONO' => '', |
||
172 | 4 | // 特店代碼 |
|
173 | 'MID' => $this->options['MID'], |
||
174 | 4 | ]; |
|
175 | 4 | ||
176 | $params = array_replace( |
||
177 | 4 | $supportedParams, |
|
178 | 4 | array_intersect_key($params, $supportedParams) |
|
179 | 4 | ); |
|
180 | 4 | ||
181 | return $this->doRequest($params, 'sync'); |
||
182 | 4 | } |
|
183 | 2 | ||
184 | 2 | /** |
|
185 | 2 | * refundTransaction. |
|
186 | * |
||
187 | * @param array $params |
||
188 | 4 | * @return array |
|
189 | 4 | */ |
|
190 | 2 | public function refundTransaction(array $params) |
|
191 | 2 | { |
|
192 | $supportedParams = [ |
||
193 | 4 | // 05:授權 51:取消授權 71:退貨授權 |
|
194 | 'TYP' => '71', |
||
195 | // 訂單編號, 由特約商店產生,不可重複,不可 包含【_】字元,英數限用大寫 |
||
196 | 'ONO' => null, |
||
197 | // 特店代碼 |
||
198 | 'MID' => $this->options['MID'], |
||
199 | // 專案資訊 |
||
200 | 'C' => null, |
||
201 | ]; |
||
202 | 1 | ||
203 | $params = array_replace( |
||
204 | $supportedParams, |
||
205 | array_intersect_key($params, $supportedParams) |
||
206 | 1 | ); |
|
207 | |||
208 | 1 | return $this->doRequest($params, 'refund'); |
|
209 | 1 | } |
|
210 | |||
211 | 1 | /** |
|
212 | 1 | * cancelTransaction. |
|
213 | 1 | * |
|
214 | 1 | * @param array $params |
|
215 | * @return array |
||
216 | 1 | */ |
|
217 | View Code Duplication | public function cancelTransaction(array $params) |
|
218 | { |
||
219 | $supportedParams = [ |
||
220 | // 訂單編號, 由特約商店產生,不可重複,不可 包含【_】字元,英數限用大寫 |
||
221 | 'ONO' => '', |
||
222 | // 特店代碼 |
||
223 | 'MID' => $this->options['MID'], |
||
224 | ]; |
||
225 | 1 | ||
226 | $params = array_replace( |
||
227 | $supportedParams, |
||
228 | array_intersect_key($params, $supportedParams) |
||
229 | 1 | ); |
|
230 | |||
231 | 1 | return $this->doRequest($params, 'cancel'); |
|
232 | } |
||
233 | 1 | ||
234 | /** |
||
235 | 1 | * @param array $params |
|
236 | 1 | * @return string |
|
237 | */ |
||
238 | 1 | public function calculateHash($params) |
|
239 | 1 | { |
|
240 | 1 | return $this->encrypter->encrypt($params); |
|
241 | 1 | } |
|
242 | |||
243 | 1 | /** |
|
244 | * verifyHash. |
||
245 | * |
||
246 | * @param array $response |
||
247 | * @return bool |
||
248 | */ |
||
249 | public function verifyHash($response) |
||
250 | { |
||
251 | // 尚未確定 |
||
252 | 1 | return empty($response['MACD']) === true |
|
253 | ? false |
||
254 | : $this->calculateHash($response) === $response['MACD']; |
||
255 | } |
||
256 | 1 | ||
257 | /** |
||
258 | 1 | * @param array $fields |
|
259 | 1 | * @return array |
|
260 | */ |
||
261 | 1 | protected function doRequest(array $fields, $type = 'sync') |
|
262 | 1 | { |
|
263 | 1 | $request = $this->messageFactory->createRequest('POST', $this->getApiEndpoint($type), [ |
|
264 | 1 | 'Content-Type' => 'application/x-www-form-urlencoded', |
|
265 | ], http_build_query($this->encrypter->encryptRequest($fields))); |
||
266 | 1 | ||
267 | $response = $this->client->send($request); |
||
268 | |||
269 | if (false === ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) { |
||
270 | throw HttpException::factory($request, $response); |
||
271 | } |
||
272 | |||
273 | 4 | return $this->parseResponse( |
|
274 | $response->getBody()->getContents() |
||
275 | 4 | ); |
|
276 | } |
||
277 | |||
278 | /** |
||
279 | * parseStr. |
||
280 | * |
||
281 | * @param string $str |
||
282 | * @return array |
||
283 | */ |
||
284 | 4 | protected function parseStr($str) |
|
285 | { |
||
286 | $response = []; |
||
287 | 4 | parse_str($str, $response); |
|
288 | 4 | ||
289 | 4 | return $response; |
|
290 | } |
||
291 | |||
292 | /** |
||
293 | * isMobile. |
||
294 | * |
||
295 | * @return bool |
||
296 | */ |
||
297 | 3 | protected function isMobile() |
|
307 | } |
||
308 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.