1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PayumTW\Mypay; |
4
|
|
|
|
5
|
|
|
use LogicException; |
6
|
|
|
use Http\Message\MessageFactory; |
7
|
|
|
use Payum\Core\HttpClientInterface; |
8
|
|
|
use Payum\Core\Exception\Http\HttpException; |
9
|
|
|
|
10
|
|
|
class Api |
11
|
|
|
{ |
12
|
|
|
const NOTIFY_TOKEN_FIELD = 'echo_4'; |
13
|
|
|
|
14
|
|
|
// 1 CREDITCARD 信用卡 |
15
|
|
|
const CREDITCARD = 'CREDITCARD'; |
16
|
|
|
// 2 RECHARGE 儲值卡 |
17
|
|
|
const RECHARGE = 'RECHARGE'; |
18
|
|
|
// 3 CSTORECODE 超商代碼 |
19
|
|
|
const CSTORECODE = 'CSTORECODE'; |
20
|
|
|
// 4 WEBATM WEBATM |
21
|
|
|
const WEBATM = 'WEBATM'; |
22
|
|
|
// 5 TELECOM 電信小額 |
23
|
|
|
const TELECOM = 'TELECOM'; |
24
|
|
|
// 6 E_COLLECTION 虛擬帳號 |
25
|
|
|
const E_COLLECTION = 'E_COLLECTION'; |
26
|
|
|
// 7 UNIONPAY 銀聯卡 |
27
|
|
|
const UNIONPAY = 'UNIONPAY'; |
28
|
|
|
// 8 SVC 點數卡 |
29
|
|
|
const SVC = 'SVC'; |
30
|
|
|
// 9 ABROAD 海外信用卡 |
31
|
|
|
const ABROAD = 'ABROAD'; |
32
|
|
|
// 10 ALIPAY 支付寶 |
33
|
|
|
const ALIPAY = 'ALIPAY'; |
34
|
|
|
// 11 SMARTPAY Smart Pay |
35
|
|
|
const SMARTPAY = 'SMARTPAY'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var \Payum\Core\HttpClientInterface |
39
|
|
|
*/ |
40
|
|
|
protected $client; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var \Http\Message\MessageFactory |
44
|
|
|
*/ |
45
|
|
|
protected $messageFactory; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $options = []; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* $encrypter. |
54
|
|
|
* |
55
|
|
|
* @var Encrypter |
56
|
|
|
*/ |
57
|
|
|
protected $encrypter; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param array $options |
61
|
|
|
* @param \Payum\Core\HttpClientInterface $client |
62
|
|
|
* @param \Http\Message\MessageFactory $messageFactory |
63
|
|
|
* @param Encrypter $encrypter |
64
|
|
|
* |
65
|
|
|
* @throws \Payum\Core\Exception\InvalidArgumentException if an option is invalid |
66
|
|
|
*/ |
67
|
7 |
|
public function __construct(array $options, HttpClientInterface $client, MessageFactory $messageFactory, Encrypter $encrypter = null) |
68
|
|
|
{ |
69
|
7 |
|
$this->options = $options; |
70
|
7 |
|
$this->client = $client; |
71
|
7 |
|
$this->messageFactory = $messageFactory; |
72
|
7 |
|
$this->encrypter = $encrypter ?: new Encrypter($this->options['key']); |
73
|
7 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* getApiEndpoint. |
77
|
|
|
* |
78
|
|
|
* @return string |
79
|
|
|
*/ |
80
|
5 |
|
public function getApiEndpoint() |
81
|
|
|
{ |
82
|
5 |
|
return $this->options['sandbox'] === false ? 'https://mypay.tw/api/init' : 'https://pay.usecase.cc/api/init'; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* createTransaction. |
87
|
|
|
* |
88
|
|
|
* @param array $params |
89
|
|
|
* @return array |
90
|
|
|
*/ |
91
|
1 |
|
public function createTransaction(array $params) |
92
|
|
|
{ |
93
|
|
|
$supportedParams = [ |
94
|
|
|
// 次特店商務代號 必要 必要 |
95
|
1 |
|
'store_uid' => $this->options['store_uid'], |
96
|
|
|
// 消費者帳號(請代入於貴特店中該消費者登記的帳號) 必要 必要 |
97
|
|
|
'user_id' => null, |
98
|
|
|
// 消費者姓名(請代入於貴特店中該消費者登記的名稱) |
99
|
|
|
'user_name' => null, |
100
|
|
|
// 消費者真實姓名 |
101
|
|
|
'user_real_name' => null, |
102
|
|
|
// 消費者帳單地址 |
103
|
|
|
'user_address' => null, |
104
|
|
|
// 消費者身份證字號 |
105
|
|
|
'user_sn' => null, |
106
|
|
|
// 消費者家用電話(白天電話) |
107
|
|
|
'user_phone' => null, |
108
|
|
|
// 行動電話國碼(預設886) |
109
|
1 |
|
'user_cellphone_code' => '886', |
110
|
|
|
// 消費者行動電話 |
111
|
|
|
'user_cellphone' => null, |
112
|
|
|
// 消費者 EMail |
113
|
|
|
'user_email' => null, |
114
|
|
|
// 消費者生日(格式為 YYYYMMDD,如 20090916) |
115
|
|
|
'user_birthday' => null, |
116
|
|
|
// 訂單總金額(如為定期定額付費,此為一期的金額) = 物品 之總價加總 折價 必要 必要 |
117
|
|
|
'cost' => null, |
118
|
|
|
// 預設交易幣別 |
119
|
1 |
|
'currency' => 'TWD', |
120
|
|
|
// 訂單編號(訂單編號建議不要重覆) 必要 必要 |
121
|
|
|
'order_id' => null, |
122
|
|
|
// 消費者來源 IP 必要 必要 |
123
|
1 |
|
'ip' => $this->options['ip'], |
124
|
|
|
// 訂單內物品數 必要 |
125
|
|
|
'item' => null, |
126
|
|
|
/* |
127
|
|
|
* 定期定額付費,期數單位: |
128
|
|
|
* W 為每週定期一次扣款; |
129
|
|
|
* M 為每月定期一次扣款; |
130
|
|
|
* S 為每季定期 |
131
|
|
|
* 一次扣款。如未使用到定期定額付費,不需傳此參數 |
132
|
|
|
*/ |
133
|
|
|
'regular' => null, |
134
|
|
|
// 總期數(如為 12 期即代入 12,如果為不限期數,請代入 0,如非定期定額付費,不需傳此參數 |
135
|
|
|
'regular_total' => null, |
136
|
|
|
// 1.定期定額式付費編號 2.定期分期式付費編號 |
137
|
|
|
'group_id' => null, |
138
|
|
|
// 票券總產生張數 |
139
|
|
|
'voucher_total_count' => null, |
140
|
|
|
// 物品總金額 |
141
|
|
|
'voucher_total_price ' => null, |
142
|
|
|
// 票券物品數 |
143
|
|
|
'voucher_item' => null, |
144
|
|
|
// 預選付費方法,如 pfn=CREDITCARD 即為信用卡付 費。多種類型可用逗號隔開,其他參數請參照附錄一。 必要 必要 |
145
|
1 |
|
'pfn' => static::CREDITCARD, |
146
|
|
|
// 交易成功後的轉址(若動態網址可以使用此方式傳遞) |
147
|
|
|
'success_returl' => null, |
148
|
|
|
// 交易失敗後的轉址(若動態網址可以使用此方式傳遞) |
149
|
|
|
'failure_returl' => null, |
150
|
|
|
// 折價(數值帶負數) |
151
|
|
|
'discount' => null, |
152
|
|
|
// 當pfn=CSTORECODE或 E_COLLECTION時,此為自訂有效使 用天數,否則以系統設定為預設有效天數 |
153
|
|
|
'limit_pay_days' => null, |
154
|
|
|
// 運費, |
155
|
|
|
'shipping_fee' => null, |
156
|
|
|
// 啟用快速結帳 |
157
|
1 |
|
'enable_quickpay' => 1, |
158
|
|
|
// 啟用電子錢包 |
159
|
1 |
|
'enable_ewallet' => 0, |
160
|
|
|
// 消費者完成電子錢包卡號綁定後 ,直接使用本參數,系統會自動 從綁定卡號扣款 若使用本參數,pfn將自動限制為 信用卡與海外信用卡兩種交易(虛 擬卡號在消費者啟用電子錢包時 ,會背景告知相關資訊, |
161
|
|
|
'virtual_pan' => null, |
162
|
|
|
// 1.支付頁面模式,mypay顯示結果 (預設) 2.背景發動扣款(直接回傳交易回 報參數) |
163
|
1 |
|
'ewallet_type' => 1, |
164
|
|
|
// 定期扣款起扣日(若未指定日期, 或小於今日則將判為當日扣) |
165
|
|
|
'regular_first_charge_date' => null, |
166
|
|
|
// 1網路交易(預設)/2實體交易 |
167
|
|
|
'transaction_type' => null, |
168
|
|
|
// 國內信用卡分期限定顯示期數(且必須服務商與 支付頁面設定有支援) |
169
|
|
|
'creditcard_installment' => null, |
170
|
|
|
// 無卡支付商品名稱代碼 |
171
|
|
|
'cardless_code' => null, |
172
|
|
|
// 1: 應稅 2:零稅率 3: 免稅 |
173
|
|
|
'invoice_ratetype' => null, |
174
|
|
|
]; |
175
|
|
|
|
176
|
1 |
|
$supportedItemParams = ['id', 'name', 'cost', 'amount']; |
177
|
1 |
|
if (isset($params['items']) === true) { |
178
|
1 |
|
$params['item'] = count($params['items']); |
179
|
1 |
|
$total = 0; |
180
|
1 |
|
foreach ($params['items'] as $key => $item) { |
181
|
1 |
|
if (empty($item['cost']) === true) { |
182
|
1 |
|
$item['cost'] = $item['price']; |
183
|
|
|
} |
184
|
1 |
|
if (empty($item['amount']) === true) { |
185
|
1 |
|
$item['amount'] = $item['quantity']; |
186
|
|
|
} |
187
|
1 |
|
foreach ($supportedItemParams as $name) { |
188
|
1 |
|
$params['i_'.$key.'_'.$name] = $item[$name]; |
189
|
|
|
} |
190
|
1 |
|
$params['i_'.$key.'_total'] = $item['cost'] * $item['amount']; |
191
|
1 |
|
$total += $params['i_'.$key.'_total']; |
192
|
|
|
} |
193
|
1 |
|
if (empty($params['cost'])) { |
194
|
1 |
|
$params['cost'] = $total; |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// 將 i_,v_,echo_ 開頭加入 $supportedParams |
199
|
1 |
|
foreach ($params as $key => $value) { |
200
|
1 |
|
if (preg_match('/(i|v|echo)_\d+/', $key)) { |
201
|
1 |
|
$supportedParams[$key] = null; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
205
|
1 |
|
$params = array_filter(array_replace( |
206
|
1 |
|
$supportedParams, |
207
|
1 |
|
array_intersect_key($params, $supportedParams) |
208
|
|
|
)); |
209
|
|
|
|
210
|
1 |
|
return $this->doRequest( |
211
|
1 |
|
$this->encrypter->encryptRequest($this->options['store_uid'], $params, 'api/orders') |
212
|
|
|
); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* getTransactionData. |
217
|
|
|
* |
218
|
|
|
* @param array $params |
219
|
|
|
* @return array |
220
|
|
|
*/ |
221
|
1 |
|
public function getTransactionData(array $params) |
222
|
|
|
{ |
223
|
|
|
$supportedParams = [ |
224
|
1 |
|
'uid' => null, |
225
|
|
|
'key' => null, |
226
|
|
|
]; |
227
|
|
|
|
228
|
1 |
|
$params = array_filter(array_replace( |
229
|
1 |
|
$supportedParams, |
230
|
1 |
|
array_intersect_key($params, $supportedParams) |
231
|
|
|
)); |
232
|
|
|
|
233
|
1 |
|
return $this->doRequest( |
234
|
1 |
|
$this->encrypter->encryptRequest($this->options['store_uid'], $params, 'api/queryorder') |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* refundTransaction. |
240
|
|
|
* |
241
|
|
|
* @param array $params |
242
|
|
|
* @return array |
243
|
|
|
*/ |
244
|
1 |
View Code Duplication |
public function refundTransaction(array $params) |
|
|
|
|
245
|
|
|
{ |
246
|
|
|
$supportedParams = [ |
247
|
1 |
|
'store_uid' => $this->options['store_uid'], |
248
|
|
|
'uid' => null, |
249
|
|
|
'key' => null, |
250
|
|
|
'cost' => null, |
251
|
|
|
]; |
252
|
|
|
|
253
|
1 |
|
$params = array_filter(array_replace( |
254
|
1 |
|
$supportedParams, |
255
|
1 |
|
array_intersect_key($params, $supportedParams) |
256
|
|
|
)); |
257
|
|
|
|
258
|
1 |
|
return $this->doRequest( |
259
|
1 |
|
$this->encrypter->encryptRequest($this->options['store_uid'], $params, 'api/refund') |
260
|
|
|
); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* cancelTransaction. |
265
|
|
|
* |
266
|
|
|
* @param array $params |
267
|
|
|
* @return array |
268
|
|
|
*/ |
269
|
1 |
View Code Duplication |
public function cancelTransaction(array $params) |
|
|
|
|
270
|
|
|
{ |
271
|
|
|
$supportedParams = [ |
272
|
1 |
|
'store_uid' => $this->options['store_uid'], |
273
|
|
|
'uid' => null, |
274
|
|
|
'key' => null, |
275
|
|
|
]; |
276
|
|
|
|
277
|
1 |
|
$params = array_filter(array_replace( |
278
|
1 |
|
$supportedParams, |
279
|
1 |
|
array_intersect_key($params, $supportedParams) |
280
|
|
|
)); |
281
|
|
|
|
282
|
1 |
|
return $this->doRequest( |
283
|
1 |
|
$this->encrypter->encryptRequest($this->options['store_uid'], $params, 'api/refundcancel') |
284
|
|
|
); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* verifyHash. |
289
|
|
|
* |
290
|
|
|
* @param array $params |
291
|
|
|
* @param array $details |
292
|
|
|
* @return bool |
293
|
|
|
*/ |
294
|
1 |
|
public function verifyHash(array $params, $details) |
295
|
|
|
{ |
296
|
1 |
|
return $params['key'] === $details['key']; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* @param array $fields |
301
|
|
|
* @return array |
302
|
|
|
*/ |
303
|
4 |
|
protected function doRequest(array $fields) |
304
|
|
|
{ |
305
|
4 |
|
$request = $this->messageFactory->createRequest('POST', $this->getApiEndpoint(), [ |
306
|
4 |
|
'Content-Type' => 'application/x-www-form-urlencoded', |
307
|
4 |
|
], http_build_query($fields)); |
308
|
|
|
|
309
|
4 |
|
$response = $this->client->send($request); |
310
|
|
|
|
311
|
4 |
|
$statusCode = $response->getStatusCode(); |
312
|
4 |
|
if (false === ($statusCode >= 200 && $statusCode < 300)) { |
313
|
|
|
throw HttpException::factory($request, $response); |
314
|
|
|
} |
315
|
|
|
|
316
|
4 |
|
$body = $response->getBody()->getContents(); |
317
|
4 |
|
$content = json_decode($body, true); |
318
|
4 |
|
if (null === $content) { |
319
|
|
|
throw new LogicException("Response content is not valid json: \n\n{$body}"); |
320
|
|
|
} |
321
|
|
|
|
322
|
4 |
|
return $content; |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
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.