Skip to content

Instantly share code, notes, and snippets.

@httpspace
Created July 3, 2020 04:42
Show Gist options
  • Save httpspace/4380e42618632ce135d16aed969bc388 to your computer and use it in GitHub Desktop.
Save httpspace/4380e42618632ce135d16aed969bc388 to your computer and use it in GitHub Desktop.
<?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