app_pay.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: stanley-king
  5. * Date: 15/12/9
  6. * Time: 上午12:25
  7. */
  8. define(WXPAY_PATH, BASE_DATA_PATH . '/api/open_wxpay');
  9. require_once(WXPAY_PATH . '/lib/OpenWxPay.Api.php');
  10. require_once(WXPAY_PATH . '/lib/OpenWxPay.Config.php');
  11. require_once(WXPAY_PATH . '/lib/OpenWxPay.Data.php');
  12. require_once(WXPAY_PATH . '/lib/OpenWxPay.Exception.php');
  13. require_once(WXPAY_PATH . '/lib/OpenWxPay.Notify.php');
  14. require_once(BASE_ROOT_PATH . '/helper/predeposit_helper.php');
  15. class app_payControl extends mbMemberControl
  16. {
  17. private static $pay_types = array('wxpay', 'alipay');
  18. const wx_notifyurl = BASE_SITE_URL . '/mobile/wxnotify.php';
  19. const ali_notifyurl = BASE_SITE_URL . '/mobile/alipay_notify_url.php';
  20. const wx_orderquery = 'https://api.mch.weixin.qq.com/pay/orderquery';
  21. const alipay_rsa_private = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL+I5/3VVhfpc3TmsUjuwc8mrZycavjuE4MV9Z+EXDvPy55PAYjDvKfMtRMCsJYgO4R0fyyb1OhvglyXUKLFajxOhUm/4K2hnI1E/+8zPbY0CU5Osr4C9sxWHtY66ugTx+O4W3e4CxYTfHn+C6EuiYJ2DWJig+Obphd8CPFYFzLxAgMBAAECgYA+og9zEytXIHEv/ixlNCZOjlBhkUjt5DSfPjQXGNpseLQWLbHLvm5X1Po1oECMpzevRcU8mizSYXyYuKaWw8XMJ7/CC6A8fSBdlUeLEfTfisurEnzeUsal2K/n+WAAFj+TUncnqYtEqCCT+9c4jVkfik7FNxjbcio9p27QKDzYwQJBAOlWs593knByuSFOVnIiphhKlZy+6MUxquxhP+3NlXVaTCHHbBl8xt3fLqLmrXPUHvAt39wJ8fAvLDuNUQgP2gkCQQDSIt7bZi2hmzqfXK9rqVyJ16o0Hwqa8Z6PYhodSSsf61h3+wxdEpDFxIV9JnPWBPhCxcX+d1VddyLZacXbKuupAkBpwFesID8II5Zv19cp5zYrsDHaVlOce4QhmXmlxxTDmOcEMCN38asXhzzVq4JVCn/zDnd0fDVgS6DaZJOi+bwxAkAJg2iheCvCsDtkMZcDgcRdvTTIbUtWnm+2QBO8la5tIIN90xDJOejx+yar9syxuMHgjAGdtptXwugB/cbmWDgZAkEAiRoMv6p7WKNU34kZqpvXMIhN6hUihquVFZDJ7wiBr1tCLw/kW3uhciCViM68FgLStuefTfssgr9+oHVw/o4GjA==";
  22. public function calc_predOp()
  23. {
  24. $pay_sn = $_GET['paysn'];
  25. $validate = new Validator();
  26. $validate->setValidate(Validator::verify_serialnum($pay_sn));
  27. $validate->setValidate(Validator::verify_number($pay_sn));
  28. $error = $validate->validate();
  29. if ($error != '') {
  30. return self::outerr(errcode::ErrParamter, $error);
  31. }
  32. $logic_payment = Logic('payment');
  33. $member_info = array('member_id' => $_SESSION['member_id'], 'member_name' => $_SESSION['member_name']);
  34. $canuse_pred = $logic_payment->calcPredeposit($pay_sn,$member_info,$avail_pred,$used_pred);
  35. if($used_pred === false) {
  36. return self::outerr(errcode::ErrOrder,"取不到订单信息.");
  37. } else {
  38. return self::outsuccess(array("available_predeposit" => $avail_pred,"canuse_predeposit" => $canuse_pred,'used_predposit' => $used_pred));
  39. }
  40. }
  41. /*
  42. * 微信获取支付数据
  43. */
  44. public function uniorderOp()
  45. {
  46. $pay_sn = $_GET['paysn'];
  47. $payment = $_GET['payment'];
  48. $vcode = $_GET['vcode'];
  49. if(empty($vcode)) {
  50. return self::outerr(errcode::ErrPayment, "抱歉,请升级到最新版本.");
  51. }
  52. if (in_array($payment, self::$pay_types) == false) {
  53. return self::outerr(errcode::ErrPayment, "err paytype {$payment}: only for wxpay,alipay");
  54. }
  55. $logic_payment = Logic('payment');
  56. // 尝试使用红包
  57. $usebonus = intval($_GET['usebonus']);
  58. $member_info = array('member_id' => $_SESSION['member_id'], 'member_name' => $_SESSION['member_name']);
  59. if (intval($usebonus) === 1) {
  60. $logic_payment->payPredepositEx($pay_sn,$member_info);
  61. }
  62. //重新计算所需支付金额
  63. $result = $logic_payment->getRealOrderInfo($pay_sn, $_SESSION['member_id']);
  64. if (intval($result['data']['api_pay_state']) != 0) {
  65. return self::outerr(errcode::ErrPayment, $result['msg']);
  66. }
  67. // 判断是否需要支付
  68. $output = array();
  69. $pay_money = intval(doubleval($result['data']['api_pay_amount']) * 100 + 0.5);
  70. if ($pay_money >= 1)
  71. {
  72. $output['need_pay'] = 1;
  73. $ret = $this->api_pay($result['data'], $payment);
  74. if ($ret != false) {
  75. $output['pay_info'] = $ret;
  76. } else {
  77. return self::outerr(errcode::ErrPayment, '支付失败.');
  78. }
  79. }
  80. else
  81. {
  82. $output['need_pay'] = 0;
  83. $result = self::update_order($pay_sn, $pay_sn, 'bonus'); // 更新订单
  84. // 推送到oms
  85. if (isset($result) && $result['state'])
  86. {
  87. if(is_pushoms())
  88. {
  89. $logic_delivery = Logic('delivery');
  90. $ret = $logic_delivery->putOrder($pay_sn, $pay_sn);
  91. if(empty($ret)) { //todo must add to 定时任务
  92. Log::record("Put order to oms error: pay_sn={$pay_sn},bonus pay.",Log::ERR);
  93. }
  94. }
  95. }
  96. }
  97. self::outsuccess($output);
  98. }
  99. /**
  100. * 支付订单查询
  101. *
  102. * 输入参数:
  103. * key: token
  104. * paysn: 支付sn
  105. * payment: 支付类型
  106. * transaction_id:
  107. */
  108. public function orderqueryOp()
  109. {
  110. $pay_sn = $_GET['paysn'];
  111. $payment = $_GET['payment'];
  112. $tractid = $_GET['transaction_id'];
  113. if ($payment == 'wxpay') {
  114. $input = new OpenWxPayOrderQuery();
  115. $input->SetOut_trade_no($pay_sn);
  116. $input->SetTransaction_id($tractid);
  117. $result = OpenWxPayApi::orderQuery($input);
  118. if (array_key_exists("return_code", $result)
  119. && array_key_exists("result_code", $result)
  120. && $result["return_code"] == "SUCCESS"
  121. && $result["result_code"] == "SUCCESS"
  122. ) {
  123. return self::outsuccess($result);
  124. }
  125. return self::outerr(errcode::ErrPayment, '支付失败.');
  126. } else {
  127. return self::outerr(errcode::ErrPayment, "错误的参数:payment = {$payment}");
  128. }
  129. }
  130. /**
  131. * 支付返回
  132. *
  133. * @param $pay_info
  134. * @param $payment
  135. * @return array
  136. */
  137. private function api_pay($pay_info, $payment)
  138. {
  139. $subject = $pay_info['subject'];
  140. $pay_sn = $pay_info['pay_sn'];
  141. $fee = intval(doubleval($pay_info['api_pay_amount']) * 100 + 0.5);
  142. $order_sn = $pay_info['order_list'][0]['order_sn'];
  143. if ($payment == 'wxpay') {
  144. return $this->uniorder($subject, $pay_sn, $fee,$order_sn);
  145. } else if ($payment === 'alipay') {
  146. $ret = array();
  147. $ret['rsa_private'] = self::alipay_rsa_private; // 私钥
  148. $ret['partner'] = '2088121219613123'; // 合作者身份ID
  149. $ret['seller_id'] = 'napheir.ao@lrlz.com'; // 签约卖家支付宝账号
  150. $ret['out_trade_no'] = $pay_sn; // 商户网站唯一订单号
  151. $ret['subject'] = $subject; // 商品名称
  152. $ret['body'] = "order_sn={$order_sn}"; // 商品详情
  153. $total_fee = intval(doubleval($pay_info['api_pay_amount']) * 100 + 0.5) / 100;
  154. $ret['total_fee'] = $total_fee;// 商品金额
  155. $ret['notify_url'] = self::ali_notifyurl; // 服务器异步通知页面路径
  156. $ret['service'] = 'mobile.securitypay.pay'; // 服务器接口名称,固定值
  157. $ret['payment_type'] = '1'; // 支付类型,固定值
  158. $ret['_input_charset'] = 'utf-8'; // 参数编码,固定值
  159. $ret['it_b_pay'] = '30m'; // 设置未付款交易的超时时间 30min
  160. $ret['return_url'] = 'http://'; // 支付宝完成请求后,当前页面跳转到商户指定页面的路径,可空
  161. return array('payment' => $payment, 'content' => $ret);
  162. }
  163. }
  164. /**
  165. * 格式化参数格式化成url参数
  166. */
  167. private function to_url_params($values)
  168. {
  169. $buff = "";
  170. foreach ($values as $k => $v) {
  171. if ($k != "sign" && $v != "" && !is_array($v)) {
  172. $buff .= $k . "=" . $v . "&";
  173. }
  174. }
  175. $buff = trim($buff, "&");
  176. return $buff;
  177. }
  178. private function make_sign($values)
  179. {
  180. //签名步骤一:按字典序排序参数
  181. ksort($values);
  182. $string = $this->to_url_params($values);
  183. //签名步骤二:在string后加入KEY
  184. $string = $string . "&key=" . OpenWxPayConfig::KEY;
  185. //签名步骤三:MD5加密
  186. $string = md5($string);
  187. //签名步骤四:所有字符转为大写
  188. $result = strtoupper($string);
  189. return $result;
  190. }
  191. /**
  192. * 更新订单状态
  193. *
  194. * @param $out_trade_no
  195. * @param $trade_no
  196. * @param $payment
  197. * @param $transaction_id
  198. * @return array
  199. */
  200. public static function update_order($out_trade_no, $trade_no, $payment)
  201. {
  202. $logic_payment = Logic('payment');
  203. $tmp = explode('|', $out_trade_no);
  204. $out_trade_no = $tmp[0];
  205. if (!empty($tmp[1])) {
  206. $order_type = $tmp[1];
  207. } else {
  208. $order_pay_info = Model('order')->getOrderPayInfo(array('pay_sn' => $out_trade_no));
  209. if (empty($order_pay_info)) {
  210. $order_type = 'v';
  211. } else {
  212. $order_type = 'r';
  213. }
  214. }
  215. if ($order_type == 'r') {
  216. $result = $logic_payment->getRealOrderInfo($out_trade_no);
  217. if (intval($result['data']['api_pay_state'])) {
  218. return array('state' => true);
  219. }
  220. $order_list = $result['data']['order_list'];
  221. $result = $logic_payment->updateRealOrder($out_trade_no, $payment, $order_list, $trade_no);
  222. } elseif ($order_type == 'v') {
  223. $result = $logic_payment->getVrOrderInfo($out_trade_no, null, true);
  224. if ($result['data']['order_state'] != ORDER_STATE_NEW) {
  225. return array('state' => true);
  226. }
  227. $result = $logic_payment->updateVrOrder($out_trade_no, $payment, $result['data'], $trade_no);
  228. }
  229. return $result;
  230. }
  231. private function toString($order)
  232. {
  233. $ret = '';
  234. foreach ($order as $key => $val) {
  235. $ret .= $key . "=" . $val;
  236. $ret .= '&';
  237. }
  238. return $ret;
  239. }
  240. public static function check_fee($pay_sn,$total_fee,&$need_pay)
  241. {
  242. $logic_payment = Logic('payment');
  243. $result = $logic_payment->getRealOrderInfo($pay_sn);
  244. if (intval($result['data']['api_pay_state']) != 0) {
  245. return false;
  246. }
  247. $need_pay = intval($result['data']['api_pay_amount'] * 100 + 0.5);
  248. $total_fee = intval($total_fee * 100 + 0.5);
  249. return ($need_pay === $total_fee);
  250. }
  251. /**
  252. * @return array
  253. * @throws OpenWxPayException
  254. */
  255. private function uniorder($body, $pay_sn, $fee,$order_sn)
  256. {
  257. Log::record("body={$body},paysn={$pay_sn},fee={$fee}", Log::DEBUG);
  258. $input = new OpenWxPayUnifiedOrder();
  259. $input->SetBody($body); //商品或支付单简要描述
  260. $input->SetAttach($pay_sn); //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
  261. $out_trade_no = $pay_sn . sprintf("%03d",mt_rand(0,999)); //给微信用的每次重新生成的商户订单SN, 避免二次支付, 数额修改导致支付失败
  262. $input->SetOut_trade_no($out_trade_no);//商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
  263. $input->SetTotal_fee("{$fee}");//订单总金额,单位为分,详见支付金额
  264. $input->SetTime_start(date("YmdHis")); //订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
  265. $input->SetTime_expire(date("YmdHis", time() + 600));//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
  266. $input->SetNotify_url(self::wx_notifyurl);
  267. $input->SetTrade_type("APP");//JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里,MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
  268. $order = OpenWxPayApi::unifiedOrder($input);
  269. $sorder = $this->toString($order);
  270. Log::record($sorder, Log::DEBUG);
  271. if (array_key_exists('err_code', $order)) {
  272. return false;
  273. }
  274. $ret = array(); {
  275. $ret['appid'] = $order["appid"];
  276. $ret['noncestr'] = OpenWxPayApi::getNonceStr();
  277. $ret['package'] = 'Sign=WXPay';
  278. $ret['partnerid'] = OpenWxPayConfig::MCHID;
  279. $ret['prepayid'] = $order['prepay_id'];
  280. $timeStamp = time();
  281. $ret['timestamp'] = "{$timeStamp}";
  282. $ret['sign'] = $this->make_sign($ret);
  283. }
  284. return $ret;
  285. }
  286. }