├── views └── default │ ├── images │ ├── logo.png │ └── alipay.webp │ ├── process.pdt │ └── settings.pdt ├── config.json ├── composer.json ├── language ├── zh_cn │ └── epay.php └── en_us │ └── epay.php ├── README.md ├── lib └── epay_sdk │ └── EpayCore.class.php └── epay.php /views/default/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshi233/blesta-gateway-epay/HEAD/views/default/images/logo.png -------------------------------------------------------------------------------- /views/default/images/alipay.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshi233/blesta-gateway-epay/HEAD/views/default/images/alipay.webp -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "name": "Epay.name", 4 | "description": "Epay.description", 5 | "authors": [ 6 | { 7 | "name": "Anshi", 8 | "url": "https://www.catserver.ca" 9 | } 10 | ], 11 | "currencies": ["CNY"], 12 | "signup_url": "https://pay.cccyun.cc" 13 | } -------------------------------------------------------------------------------- /views/default/process.pdt: -------------------------------------------------------------------------------- 1 | 4 | payment logo 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Catserver/epay", 3 | "description": "EPay gateway API", 4 | "license": "MIT", 5 | "type": "blesta-gateway-nonmerchant", 6 | "require": { 7 | "blesta/composer-installer": "~1.0" 8 | }, 9 | "authors": [ 10 | { 11 | "name": "Anshi", 12 | "homepage": "https://www.catserver.ca" 13 | } 14 | ], 15 | "config": { 16 | "allow-plugins": { 17 | "composer/installers": true, 18 | "blesta/composer-installer": true 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /language/zh_cn/epay.php: -------------------------------------------------------------------------------- 1 | 3 |
  • 4 | Form->label($this->_('Epay.meta.apiurl', true), 'apiurl'); 6 | $this->Form->fieldText('apiurl', ($meta['apiurl'] ?? null), ['id' => 'apiurl', 'class' => 'block']); 7 | ?> 8 |
  • 9 |
  • 10 | Form->label($this->_('Epay.meta.pid', true), 'pid'); 12 | $this->Form->fieldText('pid', ($meta['pid'] ?? null), ['id' => 'pid', 'class' => 'block']); 13 | ?> 14 |
  • 15 |
  • 16 | Form->label($this->_('Epay.meta.key', true), 'key'); 18 | $this->Form->fieldText('key', ($meta['key'] ?? null), ['id' => 'key', 'class' => 'block']); 19 | ?> 20 |
  • 21 | 22 | 23 |
    24 |

    _('Epay.webhook'); ?>

    25 |
    26 |
    27 |

    _('Epay.webhook_note'); ?>

    28 | 29 |
    30 | -------------------------------------------------------------------------------- /language/en_us/epay.php: -------------------------------------------------------------------------------- 1 | Settings > Payment Gateways 30 | 31 | 4. Find the EPay gateway and click the "Install" button to install it 32 | 33 | 5. You're done! 34 | 35 | # Limitation 36 | * EPay only support CNY. Please use Blesta's currency setting to convert currencies. 37 | * Each payment trascation only support one inovice order. 38 | * Currently no refund support. 39 | * No void invoice support (EPay API does not support it). 40 | 41 | # TO-DO 42 | * Any bug fix. 43 | * add refund support. 44 | 45 | # Support me 46 | Considering buying a VPS from [CatServer.ca](https://www.catserver.ca) 47 | 48 | # Reference 49 | * [blesta-stripe-universal](https://github.com/anshi233/blesta-stripe-universal) 50 | * [gateway-paypal_checkout](https://github.com/blesta/gateway-paypal_checkout) -------------------------------------------------------------------------------- /lib/epay_sdk/EpayCore.class.php: -------------------------------------------------------------------------------- 1 | pid = $config['pid']; 19 | $this->key = $config['key']; 20 | $this->submit_url = $config['apiurl'].'submit.php'; 21 | $this->mapi_url = $config['apiurl'].'mapi.php'; 22 | $this->api_url = $config['apiurl'].'api.php'; 23 | } 24 | 25 | // 发起支付(页面跳转) 26 | public function pagePay($param_tmp, $button='正在跳转'){ 27 | $param = $this->buildRequestParam($param_tmp); 28 | 29 | $html = '
    '; 30 | foreach ($param as $k=>$v) { 31 | $html.= ''; 32 | } 33 | $html .= '
    '; 34 | 35 | return $html; 36 | } 37 | 38 | // 发起支付(获取链接) 39 | public function getPayLink($param_tmp){ 40 | $param = $this->buildRequestParam($param_tmp); 41 | $url = $this->submit_url.'?'.http_build_query($param); 42 | return $url; 43 | } 44 | 45 | // 发起支付(API接口) 46 | public function apiPay($param_tmp){ 47 | $param = $this->buildRequestParam($param_tmp); 48 | $response = $this->getHttpResponse($this->mapi_url, http_build_query($param)); 49 | $arr = json_decode($response, true); 50 | return $arr; 51 | } 52 | 53 | // 异步回调验证 54 | public function verifyNotify(){ 55 | if(empty($_GET)) return false; 56 | 57 | $sign = $this->getSign($_GET); 58 | 59 | if($sign === $_GET['sign']){ 60 | $signResult = true; 61 | }else{ 62 | $signResult = false; 63 | } 64 | 65 | return $signResult; 66 | } 67 | 68 | // 同步回调验证 69 | public function verifyReturn(){ 70 | if(empty($_GET)) return false; 71 | 72 | $sign = $this->getSign($_GET); 73 | 74 | if($sign === $_GET['sign']){ 75 | $signResult = true; 76 | }else{ 77 | $signResult = false; 78 | } 79 | 80 | return $signResult; 81 | } 82 | /** 83 | * Validates the incoming POST/GET response from the gateway and verify its sign 84 | * @param array $get The GET data for this request 85 | * @param array $post The POST data for this request 86 | * @return array An array of transaction data, sets any errors using Input if the data fails to validate 87 | **/ 88 | public function verifyReturnBlesta($get){ 89 | if(empty($get)) return false; 90 | //Remove the blesta appended field '0', '1' and 'client_id' from $get 91 | unset($get['0']); 92 | unset($get['1']); 93 | unset($get['client_id']); 94 | $sign = $this->getSign($get); 95 | 96 | if($sign === $get['sign']){ 97 | $signResult = true; 98 | }else{ 99 | $signResult = false; 100 | } 101 | 102 | return $signResult; 103 | } 104 | 105 | 106 | // 查询订单支付状态 107 | public function orderStatus($trade_no){ 108 | //Add empty value check 109 | if(empty($trade_no)){ 110 | return false; 111 | } 112 | $result = $this->queryOrder($trade_no); 113 | if($result['status']==1){ 114 | return true; 115 | }else{ 116 | return false; 117 | } 118 | } 119 | 120 | // 查询订单 121 | public function queryOrder($trade_no){ 122 | $url = $this->api_url.'?act=order&pid=' . $this->pid . '&key=' . $this->key . '&trade_no=' . $trade_no; 123 | $response = $this->getHttpResponse($url); 124 | $arr = json_decode($response, true); 125 | return $arr; 126 | } 127 | 128 | // 查询商户信息 129 | public function queryMerchant($pid, $key, $api_url){ 130 | $url = $this->api_url.'?act=query&pid=' . $this->pid . '&key=' . $this->key; 131 | $response = $this->getHttpResponse($url); 132 | $arr = json_decode($response, true); 133 | return $arr; 134 | } 135 | 136 | // 订单退款 137 | public function refund($trade_no, $money){ 138 | $url = $this->api_url.'?act=refund'; 139 | $post = 'pid=' . $this->pid . '&key=' . $this->key . '&trade_no=' . $trade_no . '&money=' . $money; 140 | $response = $this->getHttpResponse($url, $post); 141 | $arr = json_decode($response, true); 142 | return $arr; 143 | } 144 | 145 | private function buildRequestParam($param){ 146 | $mysign = $this->getSign($param); 147 | $param['sign'] = $mysign; 148 | $param['sign_type'] = $this->sign_type; 149 | return $param; 150 | } 151 | 152 | // 计算签名 153 | private function getSign($param){ 154 | ksort($param); 155 | reset($param); 156 | $signstr = ''; 157 | 158 | foreach($param as $k => $v){ 159 | if($k != "sign" && $k != "sign_type" && $v!=''){ 160 | $signstr .= $k.'='.$v.'&'; 161 | } 162 | } 163 | $signstr = substr($signstr,0,-1); 164 | $signstr .= $this->key; 165 | $sign = md5($signstr); 166 | return $sign; 167 | } 168 | 169 | // 请求外部资源 170 | private function getHttpResponse($url, $post = false, $timeout = 10){ 171 | $ch = curl_init($url); 172 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 173 | //set to ture to use https 174 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 175 | //set to ture to use https 176 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); 177 | $httpheader[] = "Accept: */*"; 178 | $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; 179 | $httpheader[] = "Connection: close"; 180 | curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); 181 | curl_setopt($ch, CURLOPT_HEADER, false); 182 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 183 | if($post){ 184 | curl_setopt($ch, CURLOPT_POST, true); 185 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 186 | } 187 | $response = curl_exec($ch); 188 | curl_close($ch); 189 | return $response; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /epay.php: -------------------------------------------------------------------------------- 1 | loadConfig(dirname(__FILE__) . DS . 'config.json'); 33 | 34 | // Load components required by this gateway 35 | Loader::loadComponents($this, ['Input']); 36 | 37 | // Load the language required by this gateway 38 | Language::loadLang('epay', null, dirname(__FILE__) . DS . 'language' . DS); 39 | } 40 | 41 | /** 42 | * Sets the meta data for this particular gateway 43 | * 44 | * @param array $meta An array of meta data to set for this gateway 45 | */ 46 | public function setMeta(array $meta = null) 47 | { 48 | $this->meta = $meta; 49 | //Also set the EPay parameters 50 | $this->ePayConfig = [ 51 | 'pid' => $meta['pid'], 52 | 'key' => $meta['key'], 53 | 'apiurl' => $meta['apiurl'] 54 | ]; 55 | 56 | } 57 | 58 | /** 59 | * Create and return the view content required to modify the settings of this gateway 60 | * 61 | * @param array $meta An array of meta (settings) data belonging to this gateway 62 | * @return string HTML content containing the fields to update the meta data for this gateway 63 | */ 64 | public function getSettings(array $meta = null) 65 | { 66 | // Load the view into this object, so helpers can be automatically add to the view 67 | $this->view = new View('settings', 'default'); 68 | $this->view->setDefaultView('components' . DS . 'gateways' . DS . 'nonmerchant' . DS . 'epay' . DS); 69 | 70 | // Load the helpers required for this view 71 | Loader::loadHelpers($this, ['Form', 'Html']); 72 | 73 | $this->view->set('meta', $meta); 74 | 75 | return $this->view->fetch(); 76 | } 77 | 78 | /** 79 | * Validates the given meta (settings) data to be updated for this gateway 80 | * 81 | * @param array $meta An array of meta (settings) data to be updated for this gateway 82 | * @return array The meta data to be updated in the database for this gateway, or reset into the form on failure 83 | */ 84 | public function editSettings(array $meta) 85 | { 86 | // Set rules 87 | $rules = [ 88 | //Merchant ID 89 | 'pid' => [ 90 | //Check is pid is empty 91 | 'empty' => [ 92 | 'rule' => 'isEmpty', 93 | 'negate' => true, 94 | 'message' => Language::_('Epay.!error.pid.empty', true) 95 | ], 96 | ], 97 | //Merchant Key 98 | 'key' => [ 99 | //Check is key is empty 100 | 'empty' => [ 101 | 'rule' => 'isEmpty', 102 | 'negate' => true, 103 | 'message' => Language::_('Epay.!error.key.empty', true) 104 | ], 105 | ], 106 | //Gateway URL 107 | 'apiurl' => [ 108 | //Check is apiurl is empty 109 | 'empty' => [ 110 | 'rule' => 'isEmpty', 111 | 'negate' => true, 112 | 'message' => Language::_('Epay.!error.apiurl.empty', true) 113 | ], 114 | //Check is apiurl is valid 115 | 'valid' => [ 116 | 'rule' => [[$this, 'validateApiurl'], $meta['pid'], $meta['key']], 117 | 'message' => Language::_('Epay.!error.api.valid', true) 118 | ] 119 | ] 120 | ]; 121 | $this->Input->setRules($rules); 122 | 123 | // Validate the given meta data to ensure it meets the requirements 124 | $this->Input->validates($meta); 125 | 126 | 127 | // Return the meta data, no changes required regardless of success or failure for this gateway 128 | return $meta; 129 | } 130 | 131 | /** 132 | * Returns an array of all fields to encrypt when storing in the database 133 | * 134 | * @return array An array of the field names to encrypt when storing in the database 135 | */ 136 | public function encryptableFields() 137 | { 138 | 139 | //For debug, no need to encrypt now 140 | //return ['key']; 141 | //return empty array 142 | return []; 143 | } 144 | 145 | /** 146 | * Sets the currency code to be used for all subsequent payments 147 | * 148 | * @param string $currency The ISO 4217 currency code to be used for subsequent payments 149 | */ 150 | public function setCurrency($currency) 151 | { 152 | $this->currency = $currency; 153 | } 154 | 155 | /** 156 | * Returns all HTML markup required to render an authorization and capture payment form 157 | * 158 | * @param array $contact_info An array of contact info including: 159 | * - id The contact ID 160 | * - client_id The ID of the client this contact belongs to 161 | * - user_id The user ID this contact belongs to (if any) 162 | * - contact_type The type of contact 163 | * - contact_type_id The ID of the contact type 164 | * - first_name The first name on the contact 165 | * - last_name The last name on the contact 166 | * - title The title of the contact 167 | * - company The company name of the contact 168 | * - address1 The address 1 line of the contact 169 | * - address2 The address 2 line of the contact 170 | * - city The city of the contact 171 | * - state An array of state info including: 172 | * - code The 2 or 3-character state code 173 | * - name The local name of the country 174 | * - country An array of country info including: 175 | * - alpha2 The 2-character country code 176 | * - alpha3 The 3-cahracter country code 177 | * - name The english name of the country 178 | * - alt_name The local name of the country 179 | * - zip The zip/postal code of the contact 180 | * @param float $amount The amount to charge this contact 181 | * @param array $invoice_amounts An array of invoices, each containing: 182 | * - id The ID of the invoice being processed 183 | * - amount The amount being processed for this invoice (which is included in $amount) 184 | * @param array $options An array of options including: 185 | * - description The Description of the charge 186 | * - return_url The URL to redirect users to after a successful payment 187 | * - recur An array of recurring info including: 188 | * - amount The amount to recur 189 | * - term The term to recur 190 | * - period The recurring period (day, week, month, year, onetime) used in conjunction 191 | * with term in order to determine the next recurring payment 192 | * @return string HTML markup required to render an authorization and capture payment form 193 | */ 194 | public function buildProcess(array $contact_info, $amount, array $invoice_amounts = null, array $options = null) 195 | { 196 | // Force 1-decimal places only 197 | $amount = round($amount, 2); 198 | if (isset($options['recur']['amount'])) { 199 | $options['recur']['amount'] = round($options['recur']['amount'], 2); 200 | } 201 | 202 | //EPay only support RMB 203 | 204 | // At this line, we will load the view html file. It is the payment button. 205 | $this->view = $this->makeView('process', 'default', str_replace(ROOTWEBDIR, '', dirname(__FILE__) . DS)); 206 | 207 | // Load the models and helpers required for this view 208 | Loader::loadModels($this, ['Companies']); 209 | Loader::loadHelpers($this, ['Form', 'Html']); 210 | 211 | // Get Client Information 212 | Loader::loadModels($this, ['Contacts']); 213 | 214 | //EPay only support one invoice for each transaction 215 | //Give error if more than one invoice 216 | if(count($invoice_amounts) > 1){ 217 | $this->Input->setErrors(['api' => ['internal' => 'EPay only support one invoice for each transaction']]); 218 | return; 219 | } 220 | $out_trade_no = $invoice_amounts[0]['id']; 221 | 222 | 223 | 224 | // Initialize API 225 | $api = $this->getApi($this->ePayConfig); 226 | //2024-11-28 Found an issue that the return url is different for payment made from order page or invoice payment page. 227 | //Always use invoice payment page to make epay gateway api happy. Otherwise it will give parameter changed error. 228 | $callbackUrl = Configure::get('Blesta.gw_callback_url'); 229 | preg_match('/^(https?:\/\/[^\/]+)/', $callbackUrl, $matches); 230 | $baseUrl = $matches[1]; 231 | 232 | // For EPay, we need to don't need to create order first. 233 | // Just collect enough information and send to EPay directly, it will give us a payment link. 234 | // We will use the EPayCore class to do this. 235 | // ePayUrl is the link that we want to redirect the user to. 236 | $orderInfo = array( 237 | "pid" => $this->ePayConfig['pid'], 238 | //Type leave blank for now. We want to let user select payment method. (Alipay, WeChat Pay .etc) 239 | //TO-DO: Add a dropdown to let user select payment method. (No ETA) 240 | "type" => '', 241 | //Notify URL is the blesta websocket URL. 242 | "notify_url" => Configure::get('Blesta.gw_callback_url') . Configure::get('Blesta.company_id') . '/epay/', 243 | //Return URL is the URL that user will be redirected to after payment. 244 | //"return_url" => $options['return_url'], 245 | //TO-DO: May have issue if multi-company is used. 246 | "return_url" => $baseUrl . '/client/pay/received/epay/?client_id=' . $contact_info['client_id'], 247 | //out_trade_no is our blesta created order number(Invoice number) 248 | "out_trade_no" => $out_trade_no, 249 | //name is the product name e.g. "HK VPS Value Plan" 250 | "name" => $options['description'], 251 | //money is the price of the product in RMB!!! 252 | "money" => $amount, 253 | //use EPay API's param field to passing client_id 254 | "param" => "client_id=" . $contact_info['client_id'] 255 | ); 256 | //Log the api input 257 | $this->log('buildProcess', json_encode($orderInfo), 'input', true); 258 | //Get payment link 259 | try { 260 | $ePayUrl = $api->getPayLink($orderInfo); 261 | } catch (Exception $e) { 262 | $this->Input->setErrors(['api' => ['internal' => $e->getMessage()]]); 263 | return; 264 | } 265 | 266 | $this->view->set('epay_url', $ePayUrl); 267 | 268 | return $this->view->fetch(); 269 | 270 | } 271 | 272 | /** 273 | * Handle Verified payment result information 274 | * This function will format the raw EPay API return information to somthing Blesta can understand. 275 | * @param array $get The GET data from EPay API requests 276 | * @return array The array of transaction data 277 | */ 278 | private function handleEPayOrder(array $get){ 279 | $out_trade_no = $get['out_trade_no'] ?? null; 280 | $trade_no = $get['trade_no'] ?? null; 281 | //I will use amount not money 282 | $amount = $get['money'] ?? null; 283 | 284 | // Start process the successful payment 285 | // Get Client ID from EPay API's param field 286 | preg_match('/client_id=(\d+)/', $get['param'], $matches); 287 | if (!empty($matches) && !empty($matches[1])) { 288 | $clientId = $matches[1]; 289 | } else { 290 | // Handle the case where client_id is empty or not found 291 | $this->Input->setErrors(['api' => ['internal' => 'empty client id']]); 292 | return; 293 | } 294 | return [ 295 | 'client_id' => $clientId ?? null, 296 | 'amount' => $amount, 297 | 'currency' => 'CNY', 298 | //'invoices' => $this->unserializeInvoices($out_trade_no), 299 | 'invoices' => [['id' => $out_trade_no, 'amount' => $amount]], 300 | 'status' => 'approved', 301 | 'reference_id' => null, 302 | 'transaction_id' => $trade_no, 303 | 'parent_transaction_id' => null 304 | ]; 305 | } 306 | 307 | 308 | /** 309 | * Validates the incoming POST/GET response from the gateway to ensure it is 310 | * legitimate and can be trusted. 311 | * 312 | * @param array $get The GET data for this request 313 | * @param array $post The POST data for this request 314 | * @return array An array of transaction data, sets any errors using Input if the data fails to validate 315 | * - client_id The ID of the client that attempted the payment 316 | * - amount The amount of the payment 317 | * - currency The currency of the payment 318 | * - invoices An array of invoices and the amount the payment should be applied to (if any) including: 319 | * - id The ID of the invoice to apply to 320 | * - amount The amount to apply to the invoice 321 | * - status The status of the transaction (approved, declined, void, pending, reconciled, refunded, returned) 322 | * - reference_id The reference ID for gateway-only use with this transaction (optional) 323 | * - transaction_id The ID returned by the gateway to identify this transaction 324 | * - parent_transaction_id The ID returned by the gateway to identify this 325 | * transaction's original transaction (in the case of refunds) 326 | */ 327 | public function validate(array $get, array $post) 328 | { 329 | // Initialize API 330 | $api = $this->getApi($this->ePayConfig); 331 | //From raw get data verify EPay sign 332 | $sign_result = $api->verifyReturnBlesta($get); 333 | if($sign_result == false) { 334 | //Throw error when sign validation failed 335 | $this->Input->setErrors([ 336 | 'event' => ['invalid_sign' => Language::_('Epay.!error.event.invalid_sign', true)] 337 | ]); 338 | return; 339 | } 340 | // Discard all webhook events, except when the order is completed or approved 341 | if ($get['trade_status'] != 'TRADE_SUCCESS') { 342 | //Throw error event for unsuccessful payment result 343 | $this->Input->setErrors([ 344 | 'event' => ['unsupported' => Language::_('Epay.!error.event.unsupported', true)] 345 | ]); 346 | return; 347 | } 348 | 349 | // log the sucess payment in blesta logs 350 | $this->log('validate', json_encode($get), 'input', !empty($get)); 351 | //Tell Epay API Gateway that we have received the payment 352 | echo 'success'; 353 | 354 | 355 | return $this->handleEPayOrder($get); 356 | } 357 | 358 | /** 359 | * Returns data regarding a success transaction. This method is invoked when 360 | * a client returns from the non-merchant gateway's web site back to Blesta. 361 | * Most of the part of this function will be same as $this->validate() 362 | * however, we don't trust client since they might do the return attack. 363 | * Extra layer of security is done by requesting EPay API Gateway to confrim. 364 | * 365 | * @param array $get The GET data for this request 366 | * @param array $post The POST data for this request 367 | * @return array An array of transaction data, may set errors using Input if the data appears invalid 368 | * - client_id The ID of the client that attempted the payment 369 | * - amount The amount of the payment 370 | * - currency The currency of the payment 371 | * - invoices An array of invoices and the amount the payment should be applied to (if any) including: 372 | * - id The ID of the invoice to apply to 373 | * - amount The amount to apply to the invoice 374 | * - status The status of the transaction (approved, declined, void, pending, reconciled, refunded, returned) 375 | * - transaction_id The ID returned by the gateway to identify this transaction 376 | * - parent_transaction_id The ID returned by the gateway to identify this transaction's original transaction 377 | */ 378 | public function success(array $get, array $post) 379 | { 380 | // Initialize API 381 | $api = $this->getApi($this->ePayConfig); 382 | //From raw get data verify EPay sign 383 | $sign_result = $api->verifyReturnBlesta($get); 384 | if($sign_result == false) { 385 | //Throw error when sign validation failed 386 | $this->Input->setErrors([ 387 | 'event' => ['invalid_sign' => Language::_('Epay.!error.event.invalid_sign', true)] 388 | ]); 389 | return; 390 | } 391 | // Discard all webhook events, except when the order is completed or approved 392 | if ($get['trade_status'] != 'TRADE_SUCCESS') { 393 | //Throw error event for unsuccessful payment result 394 | $this->Input->setErrors([ 395 | 'event' => ['unsupported' => Language::_('Epay.!error.event.unsupported', true)] 396 | ]); 397 | return; 398 | } 399 | //Send a extra request to API Gateway to make sure gateway really get the payment 400 | $isPaid = $api->orderStatus($get['trade_no'] ?? null); 401 | if(!$isPaid){ 402 | //user return success but gateway not receive payment??? 403 | //Suspicous! Not accept this request. 404 | $this->Input->setErrors([ 405 | 'event' => ['fake_success_payment' => Language::_('Epay.!error.event.fake_success_payment', true)] 406 | ]); 407 | return; 408 | } 409 | 410 | 411 | // log the sucess payment in blesta logs 412 | $this->log('validate', json_encode($get), 'input', !empty($get)); 413 | return $this->handleEPayOrder($get); 414 | } 415 | 416 | /** 417 | * Refund a payment 418 | * 419 | * @param string $reference_id The reference ID for the previously submitted transaction 420 | * @param string $transaction_id The transaction ID for the previously submitted transaction 421 | * @param float $amount The amount to refund this transaction 422 | * @param string $notes Notes about the refund that may be sent to the client by the gateway 423 | * @return array An array of transaction data including: 424 | * - status The status of the transaction (approved, declined, void, pending, reconciled, refunded, returned) 425 | * - reference_id The reference ID for gateway-only use with this transaction (optional) 426 | * - transaction_id The ID returned by the remote gateway to identify this transaction 427 | * - message The message to be displayed in the interface in addition to the standard 428 | * message for this transaction status (optional) 429 | */ 430 | public function refund($reference_id, $transaction_id, $amount, $notes = null) 431 | { 432 | //TO-DO Add automatic refund feature 433 | // Method is unsupported for now 434 | if (isset($this->Input)) 435 | $this->Input->setErrors($this->getCommonError("unsupported")); 436 | } 437 | 438 | /** 439 | * Void a payment or authorization. 440 | * 441 | * @param string $reference_id The reference ID for the previously submitted transaction 442 | * @param string $transaction_id The transaction ID for the previously submitted transaction 443 | * @param string $notes Notes about the void that may be sent to the client by the gateway 444 | * @return array An array of transaction data including: 445 | * - status The status of the transaction (approved, declined, void, pending, reconciled, refunded, returned) 446 | * - reference_id The reference ID for gateway-only use with this transaction (optional) 447 | * - transaction_id The ID returned by the remote gateway to identify this transaction 448 | * - message The message to be displayed in the interface in addition to the standard 449 | * message for this transaction status (optional) 450 | */ 451 | public function void($reference_id, $transaction_id, $notes = null) 452 | { 453 | // Method is unsupported for now 454 | if (isset($this->Input)) 455 | $this->Input->setErrors($this->getCommonError("unsupported")); 456 | } 457 | 458 | /** 459 | * Loads the given API if not already loaded 460 | * 461 | * @param array $ePayConfig The EPay configuration 462 | */ 463 | private function getApi($config) 464 | { 465 | return new EpayCore($config); 466 | } 467 | 468 | /** 469 | * Validates if the provided API Key is valid 470 | * 471 | * @param string $pid The merchant ID 472 | * @param string $key The API key 473 | * @param string $apiurl the EPay gateway API URL 474 | * @return bool True if the API Key is valid, false otherwise 475 | */ 476 | public function validateConnection($pid, $key, $apiurl) 477 | { 478 | try { 479 | $ePayConfigTemp = [ 480 | 'pid' => $pid, 481 | 'key' => $key, 482 | 'apiurl' => $apiurl 483 | ]; 484 | // Initialize API 485 | $api = $this->getApi($ePayConfigTemp); 486 | $merchantInfo = $api->queryMerchant($pid, $key, $apiurl); 487 | 488 | if(!empty($merchantInfo) && !empty($merchantInfo['code'])){ 489 | if($merchantInfo['code'] == 1){ 490 | return true; 491 | }elseif($merchantInfo['code'] == -3){ 492 | //API Credential is invalid. 493 | $this->Input->setErrors(['create' => ['response' => 'EPay API Gateway return code ' . $merchantInfo['code'] . "\nPlease check you API Key and Merchant ID"]]); 494 | return false; 495 | }else{ 496 | $this->Input->setErrors(['create' => ['response' => 'EPay API Gateway return code ' . $merchantInfo['code']]]); 497 | return false; 498 | } 499 | } 500 | $this->Input->setErrors(['create' => ['response' => 'Failed to connect to EPay API Gateway']]); 501 | return false; 502 | 503 | } catch (Throwable $e) { 504 | $this->Input->setErrors(['create' => ['response' => $e->getMessage()]]); 505 | return false; 506 | } 507 | } 508 | 509 | /** 510 | * Validate pid. api url and key comes from meta data 511 | * 512 | * @param string $file The configuration file to load 513 | */ 514 | public function validatePid($pid, $key, $apiurl){ 515 | return $this->validateConnection($pid, $key, $apiurl); 516 | } 517 | /** 518 | * Validate api url. pid and key comes from meta data 519 | * 520 | * @param string $file The configuration file to load 521 | */ 522 | public function validateApiurl($apiurl, $pid, $key){ 523 | return $this->validateConnection($pid, $key, $apiurl); 524 | } 525 | /** 526 | * Validate key. pid and api url comes from meta data 527 | * 528 | * @param string $file The configuration file to load 529 | */ 530 | public function validateKey($key, $pid, $apiurl){ 531 | return $this->validateConnection($pid, $key, $apiurl); 532 | } 533 | 534 | } 535 | --------------------------------------------------------------------------------