diff --git a/src/Message/AbstractRequest.php b/src/Message/AbstractRequest.php index 94fa250..065d621 100644 --- a/src/Message/AbstractRequest.php +++ b/src/Message/AbstractRequest.php @@ -7,6 +7,22 @@ class AbstractRequest extends \Omnipay\Common\Message\AbstractRequest { + // 用戶在廠商端的伺服器編號,不可輸入中文 + protected $serverId = ''; + + + // 用戶在廠商端的會員唯一識別編號 + protected $accountId = ''; + + + // 付費方式: 此參數非必填,參數為空時將依 交易金額(Amount)和幣別 (Currency)判斷可用的付費方式 呈現給用戶選擇 + protected $paymentType = ''; + + + // 品項代碼: 此參數非必填,參數為空時將依 交易金額(Amount)和幣別 (Currency)判斷可用的付費方式 呈現給用戶選擇 + protected $itemCode = ''; + + protected $endpoint = [ 'live' => [ 'b2b' => 'https://b2b.mycard520.com.tw', @@ -50,6 +66,12 @@ public function setAppKey($value) } + public function setAmount($value) + { + return $this->setParameter('amount', sprintf('%.2f', $value), $value); + } + + public function getTradeType() { return $this->getParameter('tradeType'); @@ -62,54 +84,61 @@ public function setTradeType($value) } - public function getData() + public function getServerId() { + return $this->getParameter('serverId'); } - public function sendData($data) + public function setServerId($value) { + return $this->setParameter('serverId', $value); } - protected function createSign($type = '') + public function getAccountId() { - $preSign = ''; + return $this->getParameter('accountId'); + } - switch ($type) { - case 'token': - $preSign = - $this->getAppId() . - $this->getTransactionId() . - $this->getParameter('tradeType') . - $this->serverId . - $this->customerId . - $this->paymentType . - $this->itemCode . - strtolower(urlencode($this->getDescription())) . - $this->getAmount() . - $this->getCurrency() . - $this->sandboxMode . - $this->getAppKey(); - break; + public function setAccountId($value) + { + return $this->setParameter('accountId', $value); + } + - case 'returnHash': - $preSign = $this->httpRequest->get('ReturnCode') . - $this->httpRequest->get('PayResult') . - $this->httpRequest->get('FacTradeSeq') . - $this->httpRequest->get('PaymentType') . - $this->httpRequest->get('Amount') . - $this->httpRequest->get('Currency') . - $this->httpRequest->get('MyCardTradeNo') . - $this->httpRequest->get('MyCardType') . - $this->httpRequest->get('PromoCode') . - $this->getAppKey(); - break; + public function getPaymentType() + { + return $this->getParameter('paymentType'); + } - } - return hash('sha256', $preSign); + public function setPaymentType($value) + { + return $this->setParameter('paymentType', $value); + } + + + public function getItemCode() + { + return $this->getParameter('itemCode'); + } + + + public function setItemCode($value) + { + return $this->setParameter('itemCode', $value); + } + + + public function getData() + { + } + + + public function sendData($data) + { } } \ No newline at end of file diff --git a/src/Message/ConfirmRequest.php b/src/Message/ConfirmRequest.php new file mode 100644 index 0000000..bb84752 --- /dev/null +++ b/src/Message/ConfirmRequest.php @@ -0,0 +1,30 @@ +getEndpoint('b2b') . '/MyBillingPay/api/PaymentConfirm'; + $requestData = [ + 'AuthCode' => $this->getToken() + ]; + $response = $this->httpClient->post($endpoint, null, $requestData)->send(); + $data = json_decode($response->getBody(), true); + return $data; + } + + + public function sendData($data) + { + return new ConfirmResponse($this, $data); + } + +} \ No newline at end of file diff --git a/src/Message/ConfirmResponse.php b/src/Message/ConfirmResponse.php new file mode 100644 index 0000000..6e39671 --- /dev/null +++ b/src/Message/ConfirmResponse.php @@ -0,0 +1,54 @@ + "MBP006" + * "ReturnMsg" => "查無授權交易,交易狀態可能不符合" + * "FacTradeSeq" => "" + * "TradeSeq" => "" + * "MyCardTradeNo" => null + * "SerialId" => "" + * ] + * + */ +use Omnipay\Common\Message\AbstractResponse; + +class ConfirmResponse extends AbstractResponse +{ + + // 二次确认会失败 ReturnCode=MBP006 + public function isSuccessful() + { + return ($this->getData()['ReturnCode'] == 1) ? true : false; + } + + + public function isPaid() + { + return $this->isSuccessful(); + } + + + public function getTransactionId() + { + return $this->getData()['FacTradeSeq']; + } + + + public function getCard() + { + return $this->getData()['MyCardTradeNo']; + } + + + public function getMessage() + { + return $this->getData()['ReturnMsg']; + } + +} \ No newline at end of file diff --git a/src/Message/NotificationRequest.php b/src/Message/NotificationRequest.php index 0322830..388ee62 100644 --- a/src/Message/NotificationRequest.php +++ b/src/Message/NotificationRequest.php @@ -50,14 +50,14 @@ private function getReturnParams() $ReturnMsg = $this->httpRequest->get('ReturnMsg'); $PayResult = $this->httpRequest->get('PayResult'); // 交易结果代码 交易成功为 3; 交易失败为 0 $FacTradeSeq = $this->httpRequest->get('FacTradeSeq'); // 厂商交易序号 - $PaymentType = $this->httpRequest->get('PaymentType'); // 付费方式 - $Amount = $this->httpRequest->get('Amount'); - $Currency = $this->httpRequest->get('Currency'); - $MyCardType = $this->httpRequest->get('MyCardType'); // 通路代码 PaymentType = INGAME 时才有值 - $PromoCode = $this->httpRequest->get('PromoCode'); // 活动代码 - $Hash = $this->httpRequest->get('Hash'); // 验证码 + // $PaymentType = $this->httpRequest->get('PaymentType'); // 付费方式 + // $Amount = $this->httpRequest->get('Amount'); + // $Currency = $this->httpRequest->get('Currency'); + // $MyCardType = $this->httpRequest->get('MyCardType'); // 通路代码 PaymentType = INGAME 时才有值 + // $PromoCode = $this->httpRequest->get('PromoCode'); // 活动代码 + // $Hash = $this->httpRequest->get('Hash'); // 验证码 // 1.PaymentType=INGAME时,传MyCard卡片号码; 2.PaymentType=COSTPOINT时,传会员扣点交易序号,格式为MMS开头+数; 3.其余PaymentType为Billing小额付款交易,传Billing交易序号 - $MyCardTradeNo = $this->httpRequest->get('MyCardTradeNo'); + // $MyCardTradeNo = $this->httpRequest->get('MyCardTradeNo'); // 检查 @@ -68,8 +68,12 @@ private function getReturnParams() throw new DefaultException($ReturnMsg); } + + $token = new TokenRequest($this->httpClient, $this->httpRequest); + $token->initialize($this->getParameters()); + // 签名验证 - if ($this->createSign('returnHash') != $this->httpRequest->get('Hash')) { + if ($token->getSign('returnHash') != $this->httpRequest->get('Hash')) { throw new DefaultException('Sign Error'); } @@ -122,4 +126,28 @@ private function getNotifyParams() } + // 参考 \Omnipay\MyCard\Message\FetchRequest + public function fetchTransaction($token = '') + { + $this->response = null; + $this->setToken($token); + $fetchRequest = new FetchRequest($this->httpClient, $this->httpRequest); + $fetchRequest->initialize($this->getParameters()); + return $fetchRequest->send(); + } + + + // docs: 3.4 確認 MyCard 交易,並進行請款(Server to Server) + // 注意: 二次扣款也会失败 + public function confirmTransaction() + { + $this->response = null; + if (!$this->getToken()) { + return false; + } + $confirmRequest = new ConfirmRequest($this->httpClient, $this->httpRequest); + $confirmRequest->initialize($this->getParameters()); + return $confirmRequest->send(); + } + } \ No newline at end of file diff --git a/src/Message/NotificationResponse.php b/src/Message/NotificationResponse.php index 4abf7f9..f927561 100644 --- a/src/Message/NotificationResponse.php +++ b/src/Message/NotificationResponse.php @@ -3,10 +3,8 @@ namespace Omnipay\MyCard\Message; -use Omnipay\MyCard\Exception\DefaultException; use Omnipay\Common\Message\AbstractResponse; use Omnipay\Common\Message\NotificationInterface; -use Guzzle\Http\Client as HttpClient; class NotificationResponse extends AbstractResponse implements NotificationInterface { @@ -15,7 +13,7 @@ class NotificationResponse extends AbstractResponse implements NotificationInter protected $token; - protected $httpClient; + protected $status; public function setToken($value) @@ -26,7 +24,7 @@ public function setToken($value) public function isSuccessful() { - return $this->getData()['code'] == 1 ? true : false; + return $this->status == static::STATUS_COMPLETED; } @@ -44,7 +42,7 @@ public function getTransactionReference() public function getTransactionStatus() { - return $this->getData()['code'] == 1 ? 'completed' : 'failed'; + return $this->status; } @@ -68,38 +66,24 @@ public function success() public function confirm() { - $this->httpClient = new HttpClient('', array('curl.options' => array(CURLOPT_CONNECTTIMEOUT => 60))); - $this->data['raw'] = $this->fetchTransaction(); // type=return时, 与raw携带过来的格式相同 - return $this->confirmTransaction(); - } - - - // 参考 \Omnipay\MyCard\Message\FetchRequest - private function fetchTransaction() - { - $endpoint = $this->request->getEndpoint('b2b') . '/MyBillingPay/api/TradeQuery'; - $requestData = [ - 'AuthCode' => $this->token - ]; - $response = $this->httpClient->post($endpoint, null, $requestData)->send(); - return json_decode($response->getBody(), true); - } + // 查询 + $fetchResponse = $this->request->fetchTransaction($this->token); + $this->data['raw'] = $fetchResponse->getData(); + if ($fetchResponse->getData()['PayResult'] != 3) { // 交易成功為3; 交易失敗為0; + $this->status = static::STATUS_FAILED; + } + else { + $this->status = static::STATUS_COMPLETED; + } - // docs: 3.4 確認 MyCard 交易,並進行請款(Server to Server) - // 注意: 二次扣款也会失败 - private function confirmTransaction() - { - $endpoint = $this->request->getEndpoint('b2b') . '/MyBillingPay/api/PaymentConfirm'; - $requestData = [ - 'AuthCode' => $this->token - ]; - $response = $this->httpClient->post($endpoint, null, $requestData)->send(); - $data = json_decode($response->getBody(), true); - if ($data['ReturnCode'] != 1) { - throw new DefaultException($data['ReturnMsg']); + // 确认订单 + $confirmResponse = $this->request->confirmTransaction(); + if ($confirmResponse->getData()['ReturnCode'] == 1) { + // TODO :: 二次确认会失败 + // $this->status = static::STATUS_COMPLETED; } - return true; + return $this; } diff --git a/src/Message/PurchaseRequest.php b/src/Message/PurchaseRequest.php index 5789d2c..3a5d58c 100644 --- a/src/Message/PurchaseRequest.php +++ b/src/Message/PurchaseRequest.php @@ -3,72 +3,20 @@ namespace Omnipay\MyCard\Message; -use Omnipay\MyCard\Exception\DefaultException; - class PurchaseRequest extends AbstractRequest { - // 用戶在廠商端的伺服器編號,不可輸入中文 - protected $serverId = ''; - - - // 用戶在廠商端的會員唯一識別編號 - protected $customerId = ''; - - - // 付費方式: 此參數非必填,參數為空時將依 交易金額(Amount)和幣別 (Currency)判斷可用的付費方式 呈現給用戶選擇 - protected $paymentType = ''; - - - // 品項代碼: 此參數非必填,參數為空時將依 交易金額(Amount)和幣別 (Currency)判斷可用的付費方式 呈現給用戶選擇 - protected $itemCode = ''; - - - // 是否沙箱模式 - protected $sandboxMode = 'false'; - - - // 厂商是否自定义充值页面 - private $customPage = false; - - - protected function requestToken() - { - $endpoint = $this->getEndpoint('b2b') . '/MyBillingPay/api/AuthGlobal'; - $requestData = [ - 'FacServiceId' => $this->getAppId(), - 'FacTradeSeq' => $this->getTransactionId(), - 'TradeType' => $this->getParameter('tradeType'), - 'ServerId' => $this->serverId, // 服务器ID - 'CustomerId' => $this->customerId, // 用户ID - 'PaymentType' => $this->paymentType, - 'ItemCode' => $this->itemCode, - 'ProductName' => $this->getDescription(), - 'Amount' => $this->getAmount(), - 'Currency' => $this->getCurrency(), - 'SandBoxMode' => $this->sandboxMode, - 'Hash' => $this->createSign('token'), - ]; - $requestData = array_filter($requestData); - $httpRequest = $this->httpClient->post($endpoint, null, $requestData); - $httpResponse = $httpRequest->send(); - $body = json_decode($httpResponse->getBody(), true); - if ($body['ReturnCode'] != 1) { - throw new DefaultException($body['ReturnMsg']); - } - $this->setTransactionReference($body['TradeSeq']); - $this->customPage = ($body['InGameSaveType'] == 1) ? true : false; - return $body['AuthCode']; - } - - public function getData() { - $this->customerId = $this->getTransactionId(); - $this->sandboxMode = $this->getTestMode() ? 'true' : 'false'; - $this->setParameter('token', $this->requestToken()); + $token = new TokenRequest($this->httpClient, $this->httpRequest); + $token->initialize($this->getParameters()); + $tokenData = $token->getToken(); + $this->setToken($tokenData['AuthCode']); + $this->setTransactionReference($tokenData['TradeSeq']); + return [ - 'customPage' => $this->customPage + // 厂商是否自定义充值页面 + 'customPage' => ($tokenData['InGameSaveType'] == 1) ? true : false ]; } diff --git a/src/Message/TokenRequest.php b/src/Message/TokenRequest.php new file mode 100644 index 0000000..30eacf2 --- /dev/null +++ b/src/Message/TokenRequest.php @@ -0,0 +1,79 @@ +getEndpoint('b2b') . '/MyBillingPay/api/AuthGlobal'; + $requestData = [ + 'FacServiceId' => $this->getAppId(), + 'FacTradeSeq' => $this->getTransactionId(), + 'TradeType' => $this->getTradeType(), + 'ServerId' => $this->getServerId() ?: '', // 服务器ID + 'CustomerId' => $this->getAccountId() ?: $this->getTransactionId(), // 用户ID + 'PaymentType' => $this->getPaymentType() ?: '', + 'ItemCode' => $this->getItemCode() ?: '', + 'ProductName' => $this->getDescription(), + 'Amount' => $this->getAmount(), + 'Currency' => $this->getCurrency(), + 'SandBoxMode' => $this->getTestMode() ? 'true' : 'false', + 'Hash' => $this->getSign('token'), + ]; + $requestData = array_filter($requestData); + $httpRequest = $this->httpClient->post($endpoint, null, $requestData); + $httpResponse = $httpRequest->send(); + $body = json_decode($httpResponse->getBody(), true); + if ($body['ReturnCode'] != 1) { + throw new DefaultException($body['ReturnMsg']); + } + return $body; + } + + + public function getSign($type = '') + { + $sandboxMode = $this->getTestMode() ? 'true' : 'false'; + $preSign = ''; + + switch ($type) { + + case 'token': + $preSign = + $this->getAppId() . + $this->getTransactionId() . + $this->getTradeType() . + $this->getServerId() . + ($this->getAccountId() ?: $this->getTransactionId()) . + $this->getPaymentType() . + $this->getItemCode() . + strtolower(urlencode($this->getDescription())) . + $this->getAmount() . + $this->getCurrency() . + $sandboxMode . + $this->getAppKey(); + break; + + case 'returnHash': + $preSign = $this->httpRequest->get('ReturnCode') . + $this->httpRequest->get('PayResult') . + $this->httpRequest->get('FacTradeSeq') . + $this->httpRequest->get('PaymentType') . + $this->httpRequest->get('Amount') . + $this->httpRequest->get('Currency') . + $this->httpRequest->get('MyCardTradeNo') . + $this->httpRequest->get('MyCardType') . + $this->httpRequest->get('PromoCode') . + $this->getAppKey(); + break; + + } + + return hash('sha256', $preSign); + } + +} \ No newline at end of file