JdPay.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. namespace app\expand\controller;
  3. use app\common\service\HelperService;
  4. use app\common\service\OpensslService;
  5. use think\Validate;
  6. use think\Cache;
  7. /**
  8. * 京东支付接口
  9. * Class Alipay
  10. * @package app\expand\controller
  11. */
  12. class JdPay extends BaseAuth
  13. {
  14. private $_Account = null;
  15. private $_merchant = null;
  16. private $_notifyUrl = null;//异步通知地址
  17. private $_notifyUrlArr = [];//通知给调用方的地址
  18. private $_notifyApiCode = null;
  19. public function __construct(){
  20. parent::__construct();
  21. if($this->_inWhiteList){
  22. return true;
  23. }
  24. $this->_notifyUrl = "http://{$_SERVER['HTTP_HOST']}/v1/notifyJdPay";
  25. $this->_Account = $this->getKey($this->_apiCode);
  26. //验证是否具有访问这个接口的权限
  27. if(!isset($this->_Account['jdMerchant'])
  28. || !isset($this->_Account['jdDesKey'])
  29. || !isset($this->_Account['jdNotifyUrlArr']) ){
  30. HelperService::returnJson(['code'=>400,'msg'=>'jdPay interface unauthorized access','data'=>[]]);
  31. }
  32. $this->_merchant = $this->_Account['jdMerchant'];
  33. }
  34. /**
  35. * 平台版京东支付
  36. */
  37. public function platformJdPay(){
  38. $params = $this->_sysParams;
  39. $rule = [
  40. //'showPayStoreName|收银台展示收款商户'=>'require',
  41. 'orderNo|订单号'=>'require',
  42. 'tradeName|订单商品名'=>'require',
  43. 'amount|支付金额(分)'=>'require|number',
  44. 'orderType|支付类型(0、实物;1、虚拟)订单'=>'require|number|between:0,1',
  45. 'callbackUrl|同步回调地址'=>'require',
  46. 'userId|用户id(保证唯一即可/免登)'=>'require'
  47. ];
  48. $validate = new Validate($rule);
  49. if(!$validate->check($params)){
  50. HelperService::returnJson(['code'=>400,'msg'=>$validate->getError()]);
  51. }
  52. $requestArr = [
  53. //'payMerchant'=>"{$params['showPayStoreName']}",//收银台展示的收款商户,默认为商户号对应的商户,
  54. 'tradeNum'=>"{$params['orderNo']}",//交易流水号
  55. 'tradeName'=>"{$params['tradeName']}",//商户订单的标题/商品名称/关键字等
  56. 'tradeTime'=>date('YmdHis'),//订单生成时间。格式:“yyyyMMddHHmmss”
  57. 'amount'=>"{$params['amount']}",//金额(分),大于0
  58. 'orderType'=>"{$params['orderType']}",//订单类型,0实物;1虚拟
  59. 'currency'=>'CNY',//货币种类
  60. 'callbackUrl'=>"{$params['callbackUrl']}",//同步通知url
  61. 'notifyUrl'=>$this->_notifyUrl,//异步通知url
  62. 'userId'=>"{$params['userId']}"
  63. ];
  64. $signArr=[
  65. 'version'=>'V2.0',
  66. 'merchant'=>$this->_merchant//商户号
  67. ];
  68. $createSignArr = array_merge($signArr,$requestArr);
  69. $sign = $this->getSign($createSignArr);
  70. $signArr['sign'] = $sign;
  71. $encryRequestArr = $this->encryptQuery($requestArr);
  72. $url = 'https://h5pay.jd.com/jdpay/saveOrder';//统一下单支付
  73. $html = $this->_createHtml($url, array_merge($encryRequestArr,$signArr));
  74. HelperService::addLog($createSignArr, $url, 'JDPAY');
  75. $this->assign('html',$html);
  76. return $this->fetch('jdpay/pay');
  77. }
  78. private function _createHtml($url,$postParams){
  79. $html = "<form action='$url' id='postFrom' method='post'>";
  80. foreach($postParams as $key=>$val){
  81. $html .="<input name='{$key}' type='hidden' value='{$val}' />";
  82. }
  83. $html .= "</form>";
  84. return $html;
  85. }
  86. public function notifyJdPay(){
  87. $oldParams = $this->_oldParams;
  88. $url = HelperService::getHttpHeader().$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  89. try{
  90. $resData = $this->decryptResXml($oldParams);
  91. if($resData['result']['code']=='000000'){
  92. $tradeNum = isset($resData['tradeNum'])?$resData['tradeNum']:"";
  93. //一年之内,同一笔订单不能复现,否则屏蔽
  94. if(Cache::has("JD{$tradeNum}")){
  95. HelperService::addLog(['resData'=>$resData,'return'=>'success'], $url, 'JDPAY');
  96. die('success');
  97. }
  98. foreach($this->_notifyUrlArr as $notifyUrl){
  99. $isSsl = strpos($notifyUrl, 'https://')!==false?true:false;
  100. $postData = json_encode($resData);
  101. $times = 3;
  102. while ($times--){
  103. $res = HelperService::httpPost($notifyUrl,$postData, $isSsl);
  104. HelperService::addLog(['request'=>$postData,'res'=>$res,'times'=>$times], $notifyUrl, "JDPAY_DETAIL");
  105. if($res !== false){
  106. break;
  107. }
  108. }
  109. }
  110. Cache::set("JD{$tradeNum}",time(),365*86400);
  111. HelperService::addLog(['resData'=>$resData,'return'=>'success'], $url, 'JDPAY');
  112. die('success');
  113. }
  114. HelperService::addLog(['resData'=>$resData,'return'=>'fail'], $url, 'JDPAY');
  115. die('fail');
  116. } catch (\Exception $ex){
  117. HelperService::addLog(['resData'=>$oldParams,'return'=>$ex->getMessage()], $url, 'JDPAY');
  118. die('fail');
  119. }
  120. }
  121. private function getSign($param){
  122. ksort($param);
  123. $queryStr = $this->httpBuildQuery($param);
  124. $sha256SourceSignString = hash("sha256", $queryStr);
  125. $sign = OpensslService::encryptByPrivateKey($this->_apiCode, $sha256SourceSignString);
  126. return $sign;
  127. }
  128. private function httpBuildQuery($arr=[]){
  129. $queryStr = '';
  130. foreach($arr as $key=>$val){
  131. if($val===""){
  132. continue;
  133. }
  134. if(!empty($queryStr)){
  135. $queryStr.='&';
  136. }
  137. $queryStr .= "$key=$val";
  138. }
  139. return $queryStr;
  140. }
  141. private function encryptQuery($param){
  142. $desKey = $this->_Account['jdDesKey'];
  143. $keys = base64_decode($desKey);
  144. foreach($param as &$val){
  145. $val = OpensslService::encrypt2HexStr($keys, $val);
  146. }
  147. return $param;
  148. }
  149. private function decryptResXml($resultData){
  150. $resultXml = simplexml_load_string($resultData);
  151. $resultObj = json_decode(json_encode($resultXml),TRUE);
  152. if(!isset($resultObj["encrypt"])){
  153. die($resultXml);
  154. }
  155. $encryptXml = $resultObj["encrypt"];
  156. $encryptStr=base64_decode($encryptXml);
  157. $desKey = $this->_getJdDesKeyByMerchant($resultObj['merchant']);
  158. $keys = base64_decode($desKey);
  159. $reqBody = OpensslService::decrypt4HexStr($keys, $encryptStr);
  160. //echo "请求返回encrypt Des解密后:".$reqBody."\n";
  161. $bodyXml = simplexml_load_string($reqBody);
  162. //echo "请求返回encrypt Des解密后:".$bodyXml->saveXML()."\n";
  163. $resData = json_decode(json_encode($bodyXml),TRUE);
  164. return $resData;
  165. }
  166. /**
  167. * 遍历目录下该jd商户号是否等于回传的商户号
  168. * @param type $merchant
  169. * @return type
  170. */
  171. private function _getJdDesKeyByMerchant($merchant){
  172. $fileList = scandir("COMPANY_LIST");
  173. foreach($fileList as $fileName){
  174. $authFileName = "COMPANY_LIST/$fileName/auth.php";
  175. if(strpos($fileName,'.')===false && file_exists($authFileName)){
  176. $fileContent = require_once($authFileName);
  177. if(isset($fileContent['jdMerchant']) &&$fileContent['jdMerchant'] == $merchant){
  178. //将异步通知接口回传
  179. $this->_notifyUrlArr = $fileContent['jdNotifyUrlArr'];
  180. $this->_notifyApiCode = $fileName;
  181. return $fileContent['jdDesKey'];
  182. }
  183. }
  184. }
  185. HelperService::returnJson(['code'=>200,'msg'=>'desKey is error','data'=>[]]);
  186. }
  187. }