acp_service.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. <?php
  2. namespace com\unionpay\acp\sdk;
  3. header ( 'Content-type:text/html;charset=utf-8' );
  4. include_once 'log.class.php';
  5. include_once 'SDKConfig.php';
  6. include_once 'common.php';
  7. include_once 'cert_util.php';
  8. class AcpService {
  9. /**
  10. *
  11. * 更新证书
  12. *
  13. * Enter description here ...
  14. */
  15. public static function updateEncryptCert(&$params)
  16. {
  17. $logger = LogUtil::getLogger();
  18. // 取得证书
  19. $strCert = $params['encryptPubKeyCert'];
  20. $certType = $params['certType'];
  21. openssl_x509_read($strCert);
  22. $certInfo = openssl_x509_parse($strCert);
  23. if($certType === "01"){
  24. $logger->LogInfo ('原证书certId:'.CertUtil::getEncryptCertId().',新证书certId:'.$certInfo['serialNumber']);
  25. // 更新敏感信息加密公钥
  26. if (CertUtil::getEncryptCertId() != $certInfo['serialNumber']) {
  27. $newFileName = getBackupFileName(SDKConfig::getSDKConfig()->encryptCertPath);
  28. // 将原证书备份重命名
  29. if(!copy(SDKConfig::getSDKConfig()->encryptCertPath, $newFileName)){
  30. $logger->LogError ('原证书备份失败');
  31. return -1;
  32. }
  33. // 更新证书
  34. if(!file_put_contents(SDKConfig::getSDKConfig()->encryptCertPath, $strCert)){
  35. $logger->LogError ('更新证书失败');
  36. return -1;
  37. }
  38. $logger->LogInfo ('证书更新成功');
  39. return 1;
  40. } else {
  41. $logger->LogInfo ('证书无需更新');
  42. return 0;
  43. }
  44. } else if($certType === "02"){
  45. return 0;
  46. } else {
  47. $logger->LogError ('unknown cerType: '. $certType);
  48. return -1;
  49. }
  50. }
  51. /**
  52. * 签名
  53. * @param req 请求要素
  54. * @param resp 应答要素
  55. * @return 是否成功
  56. */
  57. static function sign(&$params) {
  58. if($params['signMethod']=='01') {
  59. return AcpService::signByCertInfo($params, SDKConfig::getSDKConfig()->signCertPath, SDKConfig::getSDKConfig()->signCertPwd);
  60. } else {
  61. return AcpService::signBySecureKey($params, SDKConfig::getSDKConfig()->secureKey);
  62. }
  63. }
  64. static function signByCertInfo(&$params, $cert_path, $cert_pwd) {
  65. $logger = LogUtil::getLogger();
  66. $logger->LogInfo ( '=====签名报文开始======' );
  67. if(isset($params['signature'])){
  68. unset($params['signature']);
  69. }
  70. $result = false;
  71. if($params['signMethod']=='01') {
  72. //证书ID
  73. $params ['certId'] = CertUtil::getSignCertIdFromPfx($cert_path, $cert_pwd);
  74. $private_key = CertUtil::getSignKeyFromPfx( $cert_path, $cert_pwd );
  75. // 转换成key=val&串
  76. $params_str = createLinkString ( $params, true, false );
  77. $logger->LogInfo ( "签名key=val&...串 >" . $params_str );
  78. if($params['version']=='5.0.0'){
  79. $params_sha1x16 = sha1 ( $params_str, FALSE );
  80. $logger->LogInfo ( "摘要sha1x16 >" . $params_sha1x16 );
  81. // 签名
  82. $result = openssl_sign ( $params_sha1x16, $signature, $private_key, OPENSSL_ALGO_SHA1);
  83. if ($result) {
  84. $signature_base64 = base64_encode ( $signature );
  85. $logger->LogInfo ( "签名串为 >" . $signature_base64 );
  86. $params ['signature'] = $signature_base64;
  87. } else {
  88. $logger->LogInfo ( ">>>>>签名失败<<<<<<<" );
  89. }
  90. } else if($params['version']=='5.1.0'){
  91. //sha256签名摘要
  92. $params_sha256x16 = hash( 'sha256',$params_str);
  93. $logger->LogInfo ( "摘要sha256x16 >" . $params_sha256x16 );
  94. // 签名
  95. $result = openssl_sign ( $params_sha256x16, $signature, $private_key, 'sha256');
  96. if ($result) {
  97. $signature_base64 = base64_encode ( $signature );
  98. $logger->LogInfo ( "签名串为 >" . $signature_base64 );
  99. $params ['signature'] = $signature_base64;
  100. } else {
  101. $logger->LogInfo ( ">>>>>签名失败<<<<<<<" );
  102. }
  103. } else {
  104. $logger->LogError ( "wrong version: " . $params['version'] );
  105. $result = false;
  106. }
  107. } else {
  108. $logger->LogError ( "signMethod不正确");
  109. $result = false;
  110. }
  111. $logger->LogInfo ( '=====签名报文结束======' );
  112. return $result;
  113. }
  114. static function signBySecureKey(&$params, $secureKey) {
  115. $logger = LogUtil::getLogger();
  116. if($secureKey == null || trim($secureKey) == '') {
  117. $logger->LogError ( "密钥没配,签名失败");
  118. return false;
  119. }
  120. $logger->LogInfo ( '=====签名报文开始======' );
  121. if($params['signMethod']=='11') {
  122. // 转换成key=val&串
  123. $params_str = createLinkString ( $params, true, false );
  124. $logger->LogInfo ( "签名key=val&...串 >" . $params_str );
  125. $params_before_sha256 = hash('sha256', $secureKey);
  126. $params_before_sha256 = $params_str.'&'.$params_before_sha256;
  127. $logger->LogDebug( "before final sha256: " . $params_before_sha256);
  128. $params_after_sha256 = hash('sha256',$params_before_sha256);
  129. $logger->LogInfo ( "签名串为 >" . $params_after_sha256 );
  130. $params ['signature'] = $params_after_sha256;
  131. $result = true;
  132. } else if($params['signMethod']=='12') {
  133. //TODO SM3
  134. $logger->LogError ( "signMethod=12未实现");
  135. $result = false;
  136. } else {
  137. $logger->LogError ( "signMethod不正确");
  138. $result = false;
  139. }
  140. $logger->LogInfo ( '=====签名报文结束======' );
  141. return $result;
  142. }
  143. /**
  144. * 验签
  145. * @param $params array 应答数组
  146. * @return 是否成功
  147. */
  148. static function validate($params) {
  149. $logger = LogUtil::getLogger();
  150. $isSuccess = false;
  151. if($params['signMethod']=='01')
  152. {
  153. $signature_str = $params ['signature'];
  154. unset ( $params ['signature'] );
  155. $params_str = createLinkString ( $params, true, false );
  156. $logger->LogInfo ( '报文去[signature] key=val&串>' . $params_str );
  157. $logger->LogInfo ( '签名原文>' . $signature_str );
  158. if($params['version']=='5.0.0'){
  159. // 公钥
  160. $public_key = CertUtil::getVerifyCertByCertId ( $params ['certId'] );
  161. $signature = base64_decode ( $signature_str );
  162. $params_sha1x16 = sha1 ( $params_str, FALSE );
  163. $logger->LogInfo ( 'sha1>' . $params_sha1x16 );
  164. $isSuccess = openssl_verify ( $params_sha1x16, $signature, $public_key, OPENSSL_ALGO_SHA1 );
  165. $logger->LogInfo ( $isSuccess ? '验签成功' : '验签失败' );
  166. } else if($params['version']=='5.1.0'){
  167. $strCert = $params['signPubKeyCert'];
  168. $strCert = CertUtil::verifyAndGetVerifyCert($strCert);
  169. if($strCert == null){
  170. $logger->LogError ("validate cert err: " . $params["signPubKeyCert"]);
  171. $isSuccess = false;
  172. } else {
  173. $params_sha256x16 = hash('sha256', $params_str);
  174. $logger->LogInfo ( 'sha256>' . $params_sha256x16 );
  175. $signature = base64_decode ( $signature_str );
  176. $isSuccess = openssl_verify ( $params_sha256x16, $signature,$strCert, "sha256" );
  177. $logger->LogInfo ( $isSuccess ? '验签成功' : '验签失败' );
  178. }
  179. } else {
  180. $logger->LogError ( "wrong version: " . $params['version'] );
  181. $isSuccess = false;
  182. }
  183. } else {
  184. $isSuccess = AcpService::validateBySecureKey($params, SDKConfig::getSDKConfig()->secureKey);
  185. }
  186. return $isSuccess;
  187. }
  188. static function validateBySecureKey($params, $secureKey) {
  189. $logger = LogUtil::getLogger();
  190. if($secureKey == null || trim($secureKey) == '') {
  191. $logger->LogError ( "密钥没配,验签失败");
  192. return false;
  193. }
  194. $isSuccess = false;
  195. $signature_str = $params ['signature'];
  196. unset ( $params ['signature'] );
  197. $params_str = createLinkString ( $params, true, false );
  198. $logger->LogInfo ( '报文去[signature] key=val&串>' . $params_str );
  199. $logger->LogInfo ( '签名原文>' . $signature_str );
  200. if($params['signMethod']=='11') {
  201. $params_before_sha256 = hash('sha256', $secureKey);
  202. $params_before_sha256 = $params_str.'&'.$params_before_sha256;
  203. $params_after_sha256 = hash('sha256',$params_before_sha256);
  204. $isSuccess = $params_after_sha256 == $signature_str;
  205. $logger->LogInfo ( $isSuccess ? '验签成功' : '验签失败' );
  206. } else if($params['signMethod']=='12') {
  207. //TODO SM3
  208. $logger->LogError ( "sm3没实现");
  209. $isSuccess = false;
  210. } else {
  211. $logger->LogError ( "signMethod不正确");
  212. $isSuccess = false;
  213. }
  214. return $isSuccess;
  215. }
  216. /**
  217. * @deprecated 5.1.0开发包已删除此方法,请直接参考5.1.0开发包中的VerifyAppData.php验签。
  218. * 对控件支付成功返回的结果信息中data域进行验签
  219. * @param $jsonData json格式数据,例如:{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==", "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}
  220. * @return 是否成功
  221. */
  222. static function validateAppResponse($jsonData) {
  223. $data = json_decode($jsonData);
  224. $sign = $data->sign;
  225. $data = $data->data;
  226. $dataMap = parseQString($data);
  227. $public_key = CertUtil::getVerifyCertByCertId( $dataMap ['cert_id'] );
  228. $signature = base64_decode ( $sign );
  229. $params_sha1x16 = sha1 ( $data, FALSE );
  230. $isSuccess = openssl_verify ( $params_sha1x16, $signature,$public_key, OPENSSL_ALGO_SHA1 );
  231. return $isSuccess;
  232. }
  233. /**
  234. * 后台交易 HttpClient通信
  235. *
  236. * @param $params
  237. * @param $url
  238. * @return mixed
  239. */
  240. static function post($params, $url) {
  241. $logger = LogUtil::getLogger();
  242. $opts = createLinkString ( $params, false, true );
  243. $logger->LogInfo ( "后台请求地址为>" . $url );
  244. $logger->LogInfo ( "后台请求报文为>" . $opts );
  245. $ch = curl_init ();
  246. curl_setopt ( $ch, CURLOPT_URL, $url );
  247. curl_setopt ( $ch, CURLOPT_POST, 1 );
  248. curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, false ); // 不验证证书
  249. curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, false ); // 不验证HOST
  250. curl_setopt ( $ch, CURLOPT_SSLVERSION, 1 ); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
  251. curl_setopt ( $ch, CURLOPT_HTTPHEADER, array (
  252. 'Content-type:application/x-www-form-urlencoded;charset=UTF-8'
  253. ) );
  254. curl_setopt ( $ch, CURLOPT_POSTFIELDS, $opts );
  255. curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
  256. $html = curl_exec ( $ch );
  257. $logger->LogInfo ( "后台返回结果为>" . $html );
  258. if(curl_errno($ch)){
  259. $errmsg = curl_error($ch);
  260. curl_close ( $ch );
  261. $logger->LogInfo ( "请求失败,报错信息>" . $errmsg );
  262. return null;
  263. }
  264. if( curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200"){
  265. $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
  266. curl_close ( $ch );
  267. $logger->LogInfo ( "请求失败,报错信息>" . $errmsg );
  268. return null;
  269. }
  270. curl_close ( $ch );
  271. $result_arr = convertStringToArray ( $html );
  272. return $result_arr;
  273. }
  274. /**
  275. * 后台交易 HttpClient通信
  276. *
  277. * @param unknown_type $params
  278. * @param unknown_type $url
  279. * @return mixed
  280. */
  281. static function get($params, $url) {
  282. $logger = LogUtil::getLogger();
  283. $opts = createLinkString ( $params, false, true );
  284. $logger->LogDebug( "后台请求地址为>" . $url ); //get的日志太多而且没啥用,设debug级别
  285. $logger->LogDebug ( "后台请求报文为>" . $opts );
  286. $ch = curl_init ();
  287. curl_setopt ( $ch, CURLOPT_URL, $url );
  288. curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, false ); // 不验证证书
  289. curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, false ); // 不验证HOST
  290. curl_setopt ( $ch, CURLOPT_SSLVERSION, 1 ); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
  291. curl_setopt ( $ch, CURLOPT_HTTPHEADER, array (
  292. 'Content-type:application/x-www-form-urlencoded;charset=UTF-8'
  293. ) );
  294. curl_setopt ( $ch, CURLOPT_POSTFIELDS, $opts );
  295. curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
  296. $html = curl_exec ( $ch );
  297. $logger->LogInfo ( "后台返回结果为>" . $html );
  298. if(curl_errno($ch)){
  299. $errmsg = curl_error($ch);
  300. curl_close ( $ch );
  301. $logger->LogDebug ( "请求失败,报错信息>" . $errmsg );
  302. return null;
  303. }
  304. if( curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200"){
  305. $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
  306. curl_close ( $ch );
  307. $logger->LogDebug ( "请求失败,报错信息>" . $errmsg );
  308. return null;
  309. }
  310. curl_close ( $ch );
  311. return $html;
  312. }
  313. static function createAutoFormHtml($params, $reqUrl) {
  314. // <body onload="javascript:document.pay_form.submit();">
  315. $encodeType = isset ( $params ['encoding'] ) ? $params ['encoding'] : 'UTF-8';
  316. $html = <<<eot
  317. <html>
  318. <head>
  319. <meta http-equiv="Content-Type" content="text/html; charset={$encodeType}" />
  320. </head>
  321. <body onload="javascript:document.pay_form.submit();">
  322. <form id="pay_form" name="pay_form" action="{$reqUrl}" method="post">
  323. eot;
  324. foreach ( $params as $key => $value ) {
  325. $html .= " <input type=\"hidden\" name=\"{$key}\" id=\"{$key}\" value=\"{$value}\" />\n";
  326. }
  327. $html .= <<<eot
  328. <!-- <input type="submit" type="hidden">-->
  329. </form>
  330. </body>
  331. </html>
  332. eot;
  333. $logger = LogUtil::getLogger();
  334. $logger->LogInfo ( "自动跳转html>" . $html );
  335. return $html;
  336. }
  337. static function getCustomerInfo($customerInfo) {
  338. if($customerInfo == null || count($customerInfo) == 0 )
  339. return "";
  340. return base64_encode ( "{" . createLinkString ( $customerInfo, false, false ) . "}" );
  341. }
  342. /**
  343. * map转换string,敏感信息加密
  344. *
  345. * @param
  346. * $customerInfo
  347. */
  348. static function getCustomerInfoWithEncrypt($customerInfo) {
  349. if($customerInfo == null || count($customerInfo) == 0 )
  350. return "";
  351. $encryptedInfo = array();
  352. foreach ( $customerInfo as $key => $value ) {
  353. if ($key == 'phoneNo' || $key == 'cvn2' || $key == 'expired' ) {
  354. //if ($key == 'phoneNo' || $key == 'cvn2' || $key == 'expired' || $key == 'certifTp' || $key == 'certifId') {
  355. $encryptedInfo [$key] = $customerInfo [$key];
  356. unset ( $customerInfo [$key] );
  357. }
  358. }
  359. if( count ($encryptedInfo) > 0 ){
  360. $encryptedInfo = createLinkString ( $encryptedInfo, false, false );
  361. $encryptedInfo = AcpService::encryptData ( $encryptedInfo, SDKConfig::getSDKConfig()->encryptCertPath );
  362. $customerInfo ['encryptedInfo'] = $encryptedInfo;
  363. }
  364. return base64_encode ( "{" . createLinkString ( $customerInfo, false, false ) . "}" );
  365. }
  366. /**
  367. * 解析customerInfo。
  368. * 为方便处理,encryptedInfo下面的信息也均转换为customerInfo子域一样方式处理,
  369. * @param unknown $customerInfostr
  370. * @return array形式ParseCustomerInfo
  371. */
  372. static function parseCustomerInfo($customerInfostr) {
  373. $customerInfostr = base64_decode($customerInfostr);
  374. $customerInfostr = substr($customerInfostr, 1, strlen($customerInfostr) - 2);
  375. $customerInfo = parseQString($customerInfostr);
  376. if(array_key_exists("encryptedInfo", $customerInfo)) {
  377. $encryptedInfoStr = $customerInfo["encryptedInfo"];
  378. unset ( $customerInfo ["encryptedInfo"] );
  379. $encryptedInfoStr = AcpService::decryptData($encryptedInfoStr);
  380. $encryptedInfo = parseQString($encryptedInfoStr);
  381. foreach ($encryptedInfo as $key => $value){
  382. $customerInfo[$key] = $value;
  383. }
  384. }
  385. return $customerInfo;
  386. }
  387. static function getEncryptCertId() {
  388. $cert_path=SDKConfig::getSDKConfig()->encryptCertPath;
  389. return CertUtil::getEncryptCertId($cert_path);
  390. }
  391. /**
  392. * 加密数据
  393. * @param string $data数据
  394. * @param string $cert_path 证书配置路径
  395. * @return unknown
  396. */
  397. static function encryptData($data, $cert_path=null) {
  398. if( $cert_path == null ) {
  399. $cert_path = SDKConfig::getSDKConfig()->encryptCertPath;
  400. }
  401. $public_key = CertUtil::getEncryptKey( $cert_path );
  402. openssl_public_encrypt ( $data, $crypted, $public_key );
  403. return base64_encode ( $crypted );
  404. }
  405. /**
  406. * 解密数据
  407. * @param string $data数据
  408. * @param string $cert_path 证书配置路径
  409. * @return unknown
  410. */
  411. static function decryptData($data, $cert_path=null, $cert_pwd=null) {
  412. if( $cert_path == null ) {
  413. $cert_path = SDKConfig::getSDKConfig()->signCertPath;
  414. $cert_pwd = SDKConfig::getSDKConfig()->signCertPwd;
  415. }
  416. $data = base64_decode ( $data );
  417. $private_key = CertUtil::getSignKeyFromPfx ( $cert_path, $cert_pwd);
  418. openssl_private_decrypt ( $data, $crypted, $private_key );
  419. return $crypted;
  420. }
  421. /**
  422. * 处理报文中的文件
  423. *
  424. * @param unknown_type $params
  425. */
  426. static function deCodeFileContent($params, $fileDirectory) {
  427. $logger = LogUtil::getLogger();
  428. if (isset ( $params ['fileContent'] )) {
  429. $logger->LogInfo ( "---------处理后台报文返回的文件---------" );
  430. $fileContent = $params ['fileContent'];
  431. if (empty ( $fileContent )) {
  432. $logger->LogInfo ( '文件内容为空' );
  433. return false;
  434. } else {
  435. // 文件内容 解压缩
  436. $content = gzuncompress ( base64_decode ( $fileContent ) );
  437. $filePath = null;
  438. if (empty ( $params ['fileName'] )) {
  439. $logger->LogInfo ( "文件名为空" );
  440. $filePath = $fileDirectory . $params ['merId'] . '_' . $params ['batchNo'] . '_' . $params ['txnTime'] . '.txt';
  441. } else {
  442. $filePath = $fileDirectory . $params ['fileName'];
  443. }
  444. $handle = fopen ( $filePath, "w+" );
  445. if (! is_writable ( $filePath )) {
  446. $logger->LogInfo ( "文件:" . $filePath . "不可写,请检查!" );
  447. return false;
  448. } else {
  449. file_put_contents ( $filePath, $content );
  450. $logger->LogInfo ( "文件位置 >:" . $filePath );
  451. }
  452. fclose ( $handle );
  453. }
  454. return true;
  455. } else {
  456. return false;
  457. }
  458. }
  459. /**
  460. * 功能:将批量文件内容使用DEFLATE压缩算法压缩,Base64编码生成字符串并返回
  461. * 适用到的交易:批量代付,批量代收,批量退货
  462. * @param unknown $path
  463. * @return boolean|string
  464. */
  465. static function enCodeFileContent($path){
  466. $file_content_base64 = '';
  467. if(!file_exists($path)){
  468. echo '文件没找到';
  469. return false;
  470. }
  471. $file_content = file_get_contents ( $path );
  472. //UTF8 去掉文本中的 bom头
  473. $BOM = chr(239).chr(187).chr(191);
  474. $file_content = str_replace($BOM,'',$file_content);
  475. $file_content_deflate = gzcompress ( $file_content );
  476. $file_content_base64 = base64_encode ( $file_content_deflate );
  477. return $file_content_base64;
  478. }
  479. public static function refund($orderNo,$payNo,$amount,$notifyUrl=''){
  480. $params = array(
  481. //以下信息非特殊情况不需要改动
  482. 'version' => SDKConfig::getSDKConfig()->version, //版本号
  483. 'encoding' => 'utf-8', //编码方式
  484. 'signMethod' => SDKConfig::getSDKConfig()->signMethod, //签名方法
  485. 'txnType' => '04', //交易类型
  486. 'txnSubType' => '00', //交易子类
  487. 'bizType' => '000201', //业务类型
  488. 'accessType' => '0', //接入类型
  489. 'channelType' => '07', //渠道类型
  490. 'backUrl' => $notifyUrl, //后台通知地址
  491. //TODO 以下信息需要填写
  492. 'orderId' => $orderNo, //商户订单号,8-32位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费,此处默认取demo演示页面传递的参数
  493. 'merId' => file_get_contents(__DIR__.'/../assets/roots/mid.txt'), //商户代码,请改成自己的测试商户号,此处默认取demo演示页面传递的参数
  494. 'origQryId' => $payNo, //原消费的queryId,可以从查询接口或者通知接口中获取,此处默认取demo演示页面传递的参数
  495. 'txnTime' => date('YmdHis'), //订单发送时间,格式为YYYYMMDDhhmmss,重新产生,不同于原消费,此处默认取demo演示页面传递的参数
  496. 'txnAmt' => $amount, //交易金额,退货总金额需要小于等于原消费
  497. // 请求方保留域,
  498. // 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
  499. // 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
  500. // 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
  501. // 'reqReserved' =>'透传信息1|透传信息2|透传信息3',
  502. // 2. 内容可能出现&={}[]"'符号时:
  503. // 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
  504. // 2) 如果对账文件没有显示要求,可做一下base64(如下)。
  505. // 注意控制数据长度,实际传输的数据长度不能超过1024位。
  506. // 查询、通知等接口解析时使用base64_decode解base64后再对数据做后续解析。
  507. // 'reqReserved' => base64_encode('任意格式的信息都可以'),
  508. );
  509. self::sign ( $params ); // 签名
  510. $url = SDKConfig::getSDKConfig()->backTransUrl;
  511. $result_arr = self::post ( $params, $url);
  512. return [
  513. is_array($result_arr) && $result_arr["respCode"] == "00",
  514. $result_arr['respMsg'],
  515. ];
  516. }
  517. }
  518. class UnionQuery{
  519. public static function query($orderId,$txnTime){
  520. $params = array(
  521. //以下信息非特殊情况不需要改动
  522. 'version' => SDKConfig::getSDKConfig()->version, //版本号
  523. 'encoding' => 'utf-8', //编码方式
  524. 'signMethod' => SDKConfig::getSDKConfig()->signMethod, //签名方法
  525. 'txnType' => '00', //交易类型
  526. 'txnSubType' => '00', //交易子类
  527. 'bizType' => '000000', //业务类型
  528. 'accessType' => '0', //接入类型
  529. 'channelType' => '07', //渠道类型
  530. //TODO 以下信息需要填写
  531. 'orderId' => $orderId, //请修改被查询的交易的订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数
  532. 'merId' => file_get_contents(__DIR__.'/../assets/roots/mid.txt'), //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数
  533. 'txnTime' => date('YmdHis',$txnTime), //请修改被查询的交易的订单发送时间,格式为YYYYMMDDhhmmss,此处默认取demo演示页面传递的参数
  534. );
  535. AcpService::sign ( $params ); // 签名
  536. $url = SDKConfig::getSDKConfig()->singleQueryUrl;
  537. $result_arr = AcpService::post ( $params, $url);
  538. return is_array($result_arr) && $result_arr["origRespCode"] == "00";
  539. }
  540. }