国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
18KB

  1. <?php
  2. if(!defined('DEDEINC')) exit('dedebiz');
  3. /**
  4. *易宝接口类
  5. */
  6. class yeepay
  7. {
  8. var $dsql;
  9. var $mid;
  10. # 业务类型
  11. # 支付请求,固定值"Buy"
  12. var $p0_Cmd = 'Buy';
  13. # 送货地址
  14. # 为"1": 需要用户将送货地址留在易宝支付系统;为"0": 不需要,默认为 "0".
  15. var $p9_SAF = "0";
  16. # 网关地址不能随便修改
  17. var $reqURL_onLine = "https://www.yeepay.com/app-merchant-proxy/node";
  18. //$reqURL_onLine = "http://tech.yeepay.com:8080/robot/debug.action";
  19. var $return_url='/plus/carbuyaction.php?dopost=return'; //返回处理地址
  20. /**
  21. * 构造函数
  22. *
  23. * @access public
  24. * @param
  25. *
  26. * @return void
  27. */
  28. function yeepay()
  29. {
  30. global $dsql;
  31. $this->dsql = $dsql;
  32. }
  33. function __construct()
  34. {
  35. $this->yeepay();
  36. }
  37. /**
  38. * 设定接口会送地址
  39. *
  40. * 例如: $this->SetReturnUrl($cfg_basehost."/tuangou/control/index.php?ac=pay&orderid=".$p2_Order)
  41. *
  42. * @param string $returnurl 会送地址
  43. * @return void
  44. */
  45. function SetReturnUrl($returnurl='')
  46. {
  47. if (!empty($returnurl))
  48. {
  49. $this->return_url = $returnurl;
  50. }
  51. }
  52. /**
  53. * 生成支付代码
  54. * @param array $order 订单信息
  55. * @param array $payment 支付方式信息
  56. */
  57. function GetCode($order, $payment)
  58. {
  59. global $cfg_basehost,$cfg_cmspath;
  60. //对于二级目录的处理
  61. if(!empty($cfg_cmspath)) $cfg_basehost = $cfg_basehost.'/'.$cfg_cmspath;
  62. # 商家设置用户购买商品的支付信息.
  63. ##易宝支付平台统一使用GBK/GB2312编码方式,参数如用到中文,请注意转码
  64. # 商户订单号,选填.
  65. ##若不为"",提交的订单号必须在自身账户交易中唯一;为""时,易宝支付会自动生成随机的商户订单号.
  66. $p2_Order = trim($order['out_trade_no']);
  67. # 支付金额,必填.
  68. ##单位:元,精确到分.
  69. $p3_Amt = $order['price'];
  70. # 交易币种,固定值"CNY".
  71. $p4_Cur = "CNY";
  72. # 商品名称
  73. ##用于支付时显示在易宝支付网关左侧的订单产品信息.
  74. $p5_Pid = trim($order['out_trade_no']);
  75. # 商品种类
  76. $p6_Pcat = 'cart';
  77. # 商品描述
  78. $p7_Pdesc = '';
  79. # 商户接收支付成功数据的地址,支付成功后易宝支付会向该地址发送两次成功通知.
  80. //$p8_Url = $cfg_basehost."/plus/carbuyaction.php?dopost=return&code=".$payment['code'];
  81. $p8_Url = $cfg_basehost.$this->return_url.'&code='.$payment['code'];
  82. # 商户扩展信息
  83. ##商户可以任意填写1K 的字符串,支付成功时将原样返回.
  84. $pa_MP = 'member';
  85. # 应答机制
  86. ##为"1": 需要应答机制;为"0": 不需要应答机制.
  87. $pr_NeedResponse = 1;
  88. # 银行编码
  89. ##默认为"",到易宝支付网关.若不需显示易宝支付的页面,直接跳转到各银行、神州行支付、骏网一卡通等支付页面,该字段可依照附录:银行列表设置参数值.
  90. $pd_FrpId = '';
  91. #调用签名函数生成签名串
  92. $hmac = $this->getReqHmacString($payment['yp_account'],$payment['yp_key'],$p2_Order,$p3_Amt,$p4_Cur,$p5_Pid,$p6_Pcat,$p7_Pdesc,$p8_Url,$pa_MP,$pd_FrpId,$pr_NeedResponse);
  93. $button = '<form target="_blank" method="post" action="'.$this->reqURL_onLine.'">
  94. <input type="hidden" value="'.$this->p0_Cmd.'" name="p0_Cmd">
  95. <input type="hidden" value="'.$payment['yp_account'].'" name="p1_MerId">
  96. <input type="hidden" value="'.$p2_Order.'" name="p2_Order">
  97. <input type="hidden" value="'.$p3_Amt.'" name="p3_Amt">
  98. <input type="hidden" value="'.$p4_Cur.'" name="p4_Cur">
  99. <input type="hidden" value="'.$p5_Pid.'" name="p5_Pid">
  100. <input type="hidden" value="'.$p6_Pcat.'" name="p6_Pcat">
  101. <input type="hidden" value="'.$p7_Pdesc.'" name="p7_Pdesc">
  102. <input type="hidden" value="'.$p8_Url.'" name="p8_Url">
  103. <input type="hidden" value="'.$this->p9_SAF.'" name="p9_SAF">
  104. <input type="hidden" value="'.$pa_MP.'" name="pa_MP">
  105. <input type="hidden" value="'.$pd_FrpId.'" name="pd_FrpId">
  106. <input type="hidden" value="'.$pr_NeedResponse.'" name="pr_NeedResponse" >
  107. <input type="hidden" value="'.$hmac.'" name="hmac">
  108. <input type="submit" value="立即使用YeePay易宝支付"></form>';
  109. /* 清空购物车 */
  110. require_once DEDEINC.'/shopcar.class.php';
  111. $cart = new MemberShops();
  112. $cart->clearItem();
  113. $cart->MakeOrders();
  114. return $button;
  115. }
  116. /**
  117. * 响应操作
  118. */
  119. function respond()
  120. {
  121. /* 引入配置文件 */
  122. $code = preg_replace( "#[^0-9a-z-]#i", "", $_REQUEST['code'] );
  123. require_once DEDEDATA.'/payment/'.$code.'.php';
  124. $p1_MerId = trim($payment['yp_account']);
  125. $merchantKey = trim($payment['yp_key']);
  126. # 解析返回参数.
  127. $return = $this->getCallBackValue($r0_Cmd, $r1_Code, $r2_TrxId, $r3_Amt, $r4_Cur, $r5_Pid, $r6_Order, $r7_Uid, $r8_MP, $r9_BType, $hmac);
  128. # 判断返回签名是否正确(True/False)
  129. $bRet = $this->CheckHmac($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType,$hmac);
  130. # 校验码正确.
  131. if($bRet)
  132. {
  133. if($r1_Code=="1")
  134. {
  135. /*判断订单类型*/
  136. if(preg_match ("/S-P[0-9]+RN[0-9]/",$r6_Order))
  137. {
  138. //获取用户mid
  139. $row = $this->dsql->GetOne("SELECT * FROM #@__shops_orders WHERE oid = '{$r6_Order}'");
  140. $this->mid = $row['userid'];
  141. $ordertype="goods";
  142. } else if (preg_match ("/M[0-9]+T[0-9]+RN[0-9]/",$r6_Order)){
  143. $row = $this->dsql->GetOne("SELECT * FROM #@__member_operation WHERE buyid = '{$r6_Order}'");
  144. //获取订单信息,检查订单的有效性
  145. if(!is_array($row)||$row['sta']==2) return $msg = "您的订单已经处理,请不要重复提交!";
  146. $ordertype = "member";
  147. $product = $row['product'];
  148. $pname= $row['pname'];
  149. $pid=$row['pid'];
  150. $this->mid = $row['mid'];
  151. } else {
  152. return $msg = "支付失败,您的订单号有问题!";
  153. }
  154. # 需要比较返回的金额与商家数据库中订单的金额是否相等,只有相等的情况下才认为是交易成功.
  155. # 并且需要对返回的处理进行事务控制,进行记录的排它性处理,防止对同一条交易重复发货的情况发生.
  156. if($r9_BType == "1" || $r9_BType == "3"){
  157. if($ordertype == "goods"){
  158. if($this->success_db($r6_Order)) return $msg = "支付成功!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  159. else return $msg = "支付失败!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  160. } else if ($ordertype=="member") {
  161. $oldinf = $this->success_mem($r6_Order,$pname,$product,$pid);
  162. return $msg = "<span style='color:#dc3545'>".$oldinf."</span><br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  163. }
  164. } else if ( $r9_BType == "2" ){
  165. #如果需要应答机制则必须回写流,以success开头,大小写不敏感.
  166. echo "success";
  167. if($ordertype=="goods"){
  168. if($this->success_db($r6_Order)) return $msg = "支付成功!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  169. else return $msg = "支付失败!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  170. } else if ($ordertype=="member") {
  171. if($this->success_mem($r6_Order,$pname,$product,$pid)) return $msg = "支付成功!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  172. else return $msg = "支付失败!<br> <a href='/'>返回主页</a> <a href='/member'>会员中心</a>";
  173. }
  174. }
  175. }
  176. } else {
  177. $this->log_result ("verify_failed");
  178. return $msg = "交易信息被篡!<br> <a href='/'>返回主页</a> ";
  179. }
  180. }
  181. #签名函数生成签名串
  182. function getReqHmacString($p1_MerId,$merchantKey,$p2_Order,$p3_Amt,$p4_Cur,$p5_Pid,$p6_Pcat,$p7_Pdesc,$p8_Url,$pa_MP,$pd_FrpId,$pr_NeedResponse)
  183. {
  184. #进行签名处理,一定按照文档中标明的签名顺序进行
  185. $sbOld = "";
  186. #加入业务类型
  187. $sbOld = $sbOld.$this->p0_Cmd;
  188. #加入商户编号
  189. $sbOld = $sbOld.$p1_MerId;
  190. #加入商户订单号
  191. $sbOld = $sbOld.$p2_Order;
  192. #加入支付金额
  193. $sbOld = $sbOld.$p3_Amt;
  194. #加入交易币种
  195. $sbOld = $sbOld.$p4_Cur;
  196. #加入商品名称
  197. $sbOld = $sbOld.$p5_Pid;
  198. #加入商品分类
  199. $sbOld = $sbOld.$p6_Pcat;
  200. #加入商品描述
  201. $sbOld = $sbOld.$p7_Pdesc;
  202. #加入商户接收支付成功数据的地址
  203. $sbOld = $sbOld.$p8_Url;
  204. #加入送货地址标识
  205. $sbOld = $sbOld.$this->p9_SAF;
  206. #加入商户扩展信息
  207. $sbOld = $sbOld.$pa_MP;
  208. #加入银行编码
  209. $sbOld = $sbOld.$pd_FrpId;
  210. #加入是否需要应答机制
  211. $sbOld = $sbOld.$pr_NeedResponse;
  212. return $this->HmacMd5($sbOld,$merchantKey);
  213. }
  214. # 取得返回串中的所有参数
  215. function getCallBackValue(&$r0_Cmd,&$r1_Code,&$r2_TrxId,&$r3_Amt,&$r4_Cur,&$r5_Pid,&$r6_Order,&$r7_Uid,&$r8_MP,&$r9_BType,&$hmac)
  216. {
  217. $r0_Cmd = $_REQUEST['r0_Cmd'];
  218. $r1_Code = $_REQUEST['r1_Code'];
  219. $r2_TrxId = $_REQUEST['r2_TrxId'];
  220. $r3_Amt = $_REQUEST['r3_Amt'];
  221. $r4_Cur = $_REQUEST['r4_Cur'];
  222. $r5_Pid = $_REQUEST['r5_Pid'];
  223. $r6_Order = $_REQUEST['r6_Order'];
  224. $r7_Uid = $_REQUEST['r7_Uid'];
  225. $r8_MP = $_REQUEST['r8_MP'];
  226. $r9_BType = $_REQUEST['r9_BType'];
  227. $hmac = $_REQUEST['hmac'];
  228. return NULL;
  229. }
  230. function CheckHmac($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType,$hmac)
  231. {
  232. if($hmac == $this->getCallbackHmacString($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType))
  233. return TRUE;
  234. else
  235. return FALSE;
  236. }
  237. function getCallbackHmacString($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType)
  238. {
  239. #取得加密前的字符串
  240. $sbOld = "";
  241. #加入商家ID
  242. $sbOld = $sbOld.$p1_MerId;
  243. #加入消息类型
  244. $sbOld = $sbOld.$r0_Cmd;
  245. #加入业务返回码
  246. $sbOld = $sbOld.$r1_Code;
  247. #加入交易ID
  248. $sbOld = $sbOld.$r2_TrxId;
  249. #加入交易金额
  250. $sbOld = $sbOld.$r3_Amt;
  251. #加入货币单位
  252. $sbOld = $sbOld.$r4_Cur;
  253. #加入产品Id
  254. $sbOld = $sbOld.$r5_Pid;
  255. #加入订单ID
  256. $sbOld = $sbOld.$r6_Order;
  257. #加入用户ID
  258. $sbOld = $sbOld.$r7_Uid;
  259. #加入商家扩展信息
  260. $sbOld = $sbOld.$r8_MP;
  261. #加入交易结果返回类型
  262. $sbOld = $sbOld.$r9_BType;
  263. return $this->HmacMd5($sbOld,$merchantKey,'gbk');
  264. }
  265. function HmacMd5($data,$key,$lang='utf-8')
  266. {
  267. //RFC 2104 HMAC implementation for php.
  268. //Creates an md5 HMAC.
  269. //Eliminates the need to install mhash to compute a HMAC
  270. //Hacked by Lance Rushing(NOTE: Hacked means written)
  271. //需要配置环境支持iconv,否则中文参数不能正常处理
  272. if($GLOBALS['cfg_soft_lang'] != 'utf-8' || $lang!='utf-8')
  273. {
  274. $key = gb2utf8($key);
  275. $data = gb2utf8($data);
  276. }
  277. $b = 64; //byte length for md5
  278. if (strlen($key) > $b) {
  279. $key = pack("H*",md5($key));
  280. }
  281. $key = str_pad($key, $b, chr(0x00));
  282. $ipad = str_pad('', $b, chr(0x36));
  283. $opad = str_pad('', $b, chr(0x5c));
  284. $k_ipad = $key ^ $ipad ;
  285. $k_opad = $key ^ $opad;
  286. return md5($k_opad.pack("H*",md5($k_ipad.$data)));
  287. }
  288. /*处理物品交易*/
  289. function success_db($order_sn)
  290. {
  291. //获取订单信息,检查订单的有效性
  292. $row = $this->dsql->GetOne("SELECT state FROM #@__shops_orders WHERE oid='$order_sn' ");
  293. if($row['state'] > 0)
  294. {
  295. return TRUE;
  296. }
  297. /* 改变订单状态_支付成功 */
  298. $sql = "UPDATE `#@__shops_orders` SET `state`='1' WHERE `oid`='$order_sn' AND `userid`='".$this->mid."'";
  299. if($this->dsql->ExecuteNoneQuery($sql))
  300. {
  301. $this->log_result("verify_success,订单号:".$order_sn); //将验证结果存入文件
  302. return TRUE;
  303. } else {
  304. $this->log_result ("verify_failed,订单号:".$order_sn);//将验证结果存入文件
  305. return FALSE;
  306. }
  307. }
  308. /*处理点卡,会员升级*/
  309. function success_mem($order_sn,$pname,$product,$pid)
  310. {
  311. //更新交易状态为已付款
  312. $sql = "UPDATE `#@__member_operation` SET `sta`='1' WHERE `buyid`='$order_sn' AND `mid`='".$this->mid."'";
  313. $this->dsql->ExecuteNoneQuery($sql);
  314. /* 改变点卡订单状态_支付成功 */
  315. if($product=="card")
  316. {
  317. $row = $this->dsql->GetOne("SELECT cardid FROM #@__moneycard_record WHERE ctid='$pid' AND isexp='0' ");;
  318. //如果找不到某种类型的卡,直接为用户增加金币
  319. if(!is_array($row))
  320. {
  321. $nrow = $this->dsql->GetOne("SELECT num FROM #@__moneycard_type WHERE pname = '{$pname}'");
  322. $dnum = $nrow['num'];
  323. $sql1 = "UPDATE `#@__member` SET `money`=money+'{$nrow['num']}' WHERE `mid`='".$this->mid."'";
  324. $oldinf ="已经充值了".$nrow['num']."金币到您的帐号";
  325. } else {
  326. $cardid = $row['cardid'];
  327. $sql1=" UPDATE #@__moneycard_record SET uid='".$this->mid."',isexp='1',utime='".time()."' WHERE cardid='$cardid' ";
  328. $oldinf='您的充值密码是:<span style="color:color:#dc3545">'.$cardid.'</span>';
  329. }
  330. //更新交易状态为已关闭
  331. $sql2=" UPDATE #@__member_operation SET sta=2,oldinfo='$oldinf' WHERE buyid='$order_sn'";
  332. if($this->dsql->ExecuteNoneQuery($sql1) && $this->dsql->ExecuteNoneQuery($sql2))
  333. {
  334. $this->log_result("verify_success,订单号:".$order_sn); //将验证结果存入文件
  335. return $oldinf;
  336. } else {
  337. $this->log_result ("verify_failed,订单号:".$order_sn);//将验证结果存入文件
  338. return "支付失败";
  339. }
  340. /* 改变会员订单状态_支付成功 */
  341. } else if ( $product=="member" ){
  342. $row = $this->dsql->GetOne("SELECT `rank`,exptime FROM `#@__member_type` WHERE aid='$pid' ");
  343. $rank = $row['rank'];
  344. $exptime = $row['exptime'];
  345. /*计算原来升级剩余的天数*/
  346. $rs = $this->dsql->GetOne("SELECT uptime,exptime FROM `#@__member` WHERE mid='".$this->mid."'");
  347. if($rs['uptime']!=0 && $rs['exptime']!=0 )
  348. {
  349. $nowtime = time();
  350. $mhasDay = $rs['exptime'] - ceil(($nowtime - $rs['uptime'])/3600/24) + 1;
  351. $mhasDay=($mhasDay>0)? $mhasDay : 0;
  352. }
  353. //获取会员默认级别的金币和积分数
  354. $memrank = $this->dsql->GetOne("SELECT money,scores FROM `#@__arcrank` WHERE `rank`='$rank'");
  355. //更新会员信息
  356. $sql1 = " UPDATE `#@__member` SET `rank`='$rank',money=money+'{$memrank['money']}',
  357. scores=scores+'{$memrank['scores']}',exptime='$exptime'+'$mhasDay',uptime='".time()."'
  358. WHERE mid='".$this->mid."'";
  359. //更新交易状态为已关闭
  360. $sql2=" UPDATE `#@__member_operation` SET sta='2',oldinfo='会员升级成功!' WHERE buyid='$order_sn' ";
  361. if($this->dsql->ExecuteNoneQuery($sql1) && $this->dsql->ExecuteNoneQuery($sql2))
  362. {
  363. $this->log_result("verify_success,订单号:".$order_sn); //将验证结果存入文件
  364. return "会员升级成功";
  365. } else {
  366. $this->log_result ("verify_failed,订单号:".$order_sn);//将验证结果存入文件
  367. return "会员升级失败";
  368. }
  369. }
  370. }
  371. function log_result($word) {
  372. global $cfg_cmspath;
  373. $fp = fopen(dirname(__FILE__)."/../../data/payment/log.txt","a");
  374. flock($fp, LOCK_EX) ;
  375. fwrite($fp,$word.",执行日期:".strftime("%Y-%m-%d %H:%I:%S",time())."\r\n");
  376. flock($fp, LOCK_UN);
  377. fclose($fp);
  378. }
  379. }//End API