This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Service\Pay; | |
/** | |
* TaiwanPay | |
*/ | |
class TaiwanPay | |
{ | |
/** | |
* 測試端點 | |
*/ | |
const TEST_END_POINT = "https://www.focas-test.fisc.com.tw/FOCAS_WS/API20/QRP/V2"; | |
/** | |
* 營運端點 | |
*/ | |
const MAIN_END_POINT = "https://www.focas.fisc.com.tw/FOCAS_WS/API20/QRP/V2"; | |
/** | |
* 端末代碼 | |
* @var string | |
*/ | |
private $terminalId; | |
/** | |
* 特店代碼 | |
* @var string | |
*/ | |
private $merchantId; | |
/** | |
* 交易類型 | |
* @var integer | |
*/ | |
private $txnType; | |
/** | |
* AES IV值 | |
* @var string | |
*/ | |
private $aesIV; | |
/** | |
* AES KEY | |
* @var string | |
*/ | |
private $aesKey; | |
/** | |
* 返回網址 | |
*/ | |
private $returnUrl; | |
/** | |
* 是否為測試 | |
* @var boolean | |
*/ | |
public $isTest; | |
/** | |
* 事業名稱 | |
* @var string | |
*/ | |
public $careerName; | |
/** | |
* 交易金額 | |
* @var integer | |
*/ | |
public $price; | |
/** | |
* 訂單編號 | |
* @var string | |
*/ | |
public $orderNumber; | |
/** | |
* 安全碼 | |
* @var string | |
*/ | |
public $secureCode; | |
/** | |
* 收單行代碼 | |
* @var string | |
*/ | |
public $acqBank; | |
/** | |
* QRCode 到期時間 | |
* @var string | |
*/ | |
public $qrcodeExpiredAt; | |
/** | |
* 訂單名稱 | |
* @var string | |
*/ | |
public $orderName; | |
function __construct() | |
{ | |
$this->isTest = false; | |
} | |
/** | |
* 是否在測試 | |
* @param boolean $enable 開關 | |
* @return TaiwanPay 自身類別物件 | |
*/ | |
public function isDebug($enable = true) : TaiwanPay | |
{ | |
$this->isTest = $enable; | |
return $this; | |
} | |
/** | |
* 設置屬性 | |
* @param array $datas 設定訂單資料 | |
* @return TaiwanPay 自身類別物件 | |
*/ | |
public function setOrderDatas(array $datas) : TaiwanPay | |
{ | |
if (isset($datas['terminalId'])) $this->terminalId = $datas['terminalId']; | |
if (isset($datas['merchantId'])) $this->merchantId = $datas['merchantId']; | |
if (isset($datas['txnType'])) $this->txnType = $datas['txnType']; | |
if (isset($datas['aesIV'])) $this->aesIV = $datas['aesIV']; | |
if (isset($datas['aesKey'])) $this->aesKey = $datas['aesKey']; | |
if (isset($datas['returnUrl'])) $this->returnUrl = $datas['returnUrl']; | |
if (isset($datas['careerName'])) $this->careerName = $datas['careerName']; | |
if (isset($datas['price'])) $this->price = $datas['price']; | |
if (isset($datas['orderNumber'])) $this->orderNumber = $datas['orderNumber']; | |
if (isset($datas['secureCode'])) $this->secureCode = $datas['secureCode']; | |
if (isset($datas['acqBank'])) $this->acqBank = $datas['acqBank']; | |
if (isset($datas['qrcodeExpiredAt'])) $this->qrcodeExpiredAt = $datas['qrcodeExpiredAt']; | |
if (isset($datas['orderName'])) $this->orderName = $datas['orderName']; | |
return $this; | |
} | |
/** | |
* 創建訂單 | |
* @param boolean $isQrcode 是否為 QRCode | |
* @return boolean 是否完成 | |
*/ | |
public function createOrder($isQrcode = true) | |
{ | |
$qrcodeScheme = urlencode("TWQRP://{$this->careerName}/158/01/V1?D1={$this->price}&D3={$this->secureCode}&D11={$this->acqBank}&D12={$this->qrcodeExpiredAt}&OprodNumber={$this->orderName}"); | |
$qrcodeScheme = "TWQRP://星九客咖啡/158/01/V1?D1=12500&D3=AVnVbcN9xxRv&D10=901&D11=00,00600611122233344400000001;01,00600611122233344400000001;04,00800899887766554400000001&D12=20170630130000&OprodNumber=拿鐵"; | |
return $this->encodeQrcode($qrcodeScheme); | |
$datas = [ | |
// 交易類型 | |
"acqBank" => $this->acqBank, | |
"encQRCode" => $this->encodeQrcode($qrcodeScheme), | |
"encRetURL" => $this->encodeQrcode($this->returnUrl, true), | |
"merchantId" => $this->merchantId, | |
"orderNumber" => $this->orderNumber, | |
"terminalId" => $this->terminalId, | |
"txnType" => $this->txnType, | |
]; | |
$datas["verifyCode"] = $this->sign($datas); | |
return $this->postData("/WebToAppReq", $datas); | |
} | |
/** | |
* QRCode 加密 | |
* @return string 加密後 Base64 -> UrlEncode | |
*/ | |
public function encodeQrcode(string $unsignedData, $isUrl = false) : string | |
{ | |
$lenDec = dechex(strlen(urlencode($unsignedData))); | |
$lenHex = "0x" . $this->padding($lenDec, ($isUrl) ? 6 : 4); | |
$inputStr = strtoupper($lenHex . bin2hex(utf8_encode(urlencode($unsignedData)))); | |
// 這邊不對, 待處理 | |
return $signed = openssl_encrypt($inputStr, 'aes-256-cbc', $this->aesKey, 0, $this->aesIV); | |
return $signed; | |
return urlencode(base64_encode("0x02" . $signed)); | |
} | |
/** | |
* 位元補零 | |
* @param string $str 待補字串 | |
* @param int $paddingTo 補齊到幾位元 | |
* @return string 字串結果 | |
*/ | |
public static function padding(string $str, int $paddingTo) : string | |
{ | |
for ($i = strlen($str); $i < $paddingTo; $i++) { | |
$str = "0" . $str; | |
} | |
return $str; | |
} | |
/** | |
* 簽名 | |
* @param array $unsignedDatas [description] | |
* @return [type] [description] | |
*/ | |
public function sign(array $unsignedDatas) | |
{ | |
ksort($unsignedDatas); | |
$str = implode("", $unsignedDatas); | |
return hash("sha256", $str . $this->aesKey); | |
} | |
/** | |
* 發送 HTTP Request | |
* @param string $path 路徑 | |
* @param array $datas 發送資料 | |
* @return string 回應 | |
*/ | |
private function postData(string $path, $datas = []) | |
{ | |
if ($this->isTest) $url = $this::TEST_END_POINT . $path; | |
else $url = $this::MAIN_END_POINT . $path; | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, $url); | |
curl_setopt($ch, CURLOPT_POST, true); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($datas)); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); | |
$output = curl_exec($ch); | |
curl_close($ch); | |
return $output; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment