$v) { if(is_array($v) || isEmpty($v) || $k == 'sign' || $k == 'sign_type') continue; $signStr .= $k . '=' . $v . '&'; } $signStr = substr($signStr, 0, -1); return $signStr; } //生成签名 static public function makeSign($data, $md5key) { $sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5'; $signStr = self::getSignContent($data); if($sign_type == 'RSA'){ global $conf; $private_key = base64ToPem($conf['private_key'], 'PRIVATE KEY'); $pkey = openssl_pkey_get_private($private_key); if(!$pkey) return false; openssl_sign($signStr, $sign, $pkey, OPENSSL_ALGO_SHA256); return base64_encode($sign); }else{ $sign = md5($signStr . $md5key); return $sign; } } //验证签名 static public function verifySign($data, $md5key, $publicKey) { if(!isset($data['sign'])) throw new Exception('缺少签名参数'); $sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5'; if($sign_type == 'RSA'){ $public_key = base64ToPem($publicKey, 'PUBLIC KEY'); $pkey = openssl_pkey_get_public($public_key); if(!$pkey) throw new Exception('签名校验失败,商户公钥错误'); $signStr = self::getSignContent($data); $result = openssl_verify($signStr, base64_decode($data['sign']), $pkey, OPENSSL_ALGO_SHA256); return $result === 1; }else{ $sign = self::makeSign($data, $md5key); return $sign === $data['sign']; } } // 页面支付返回信息 static public function echoDefault($result){ global $cdnpublic,$order,$conf,$sitename,$ordername,$siteurl; $type = $result['type']; if(!$type) return false; switch($type){ case 'jump': //跳转 $html_text = ''; if(isset($result['submit']) && $result['submit']){ submitTemplate($html_text); }else{ echo $html_text; } break; case 'html': //显示html $html_text = $result['data']; if(isset($result['submit']) && $result['submit'] && substr($html_text, 0, 6) == '
0, 'trade_no'=>TRADE_NO]; switch($type){ case 'jump': //跳转URL $json['pay_type'] = 'jump'; $json['pay_info'] = $result['url']; break; case 'html': //显示html跳转 $json['pay_type'] = 'html'; $json['pay_info'] = $result['data']; break; case 'qrcode': //扫码支付 $json['pay_type'] = 'qrcode'; $json['pay_info'] = $result['url']; break; case 'scheme': //小程序H5跳转 $json['pay_type'] = 'urlscheme'; $json['pay_info'] = $result['url']; break; case 'jsapi': //JSAPI支付 $json['pay_type'] = 'jsapi'; $json['pay_info'] = $result['data']; break; case 'app': //APP支付 $json['pay_type'] = 'app'; $json['pay_info'] = $result['data']; break; case 'scan': //付款码支付 $json['pay_type'] = 'scan'; $json['pay_info'] = $result['data']; break; case 'wxplugin': //微信小程序插件支付 $json['pay_type'] = 'wxplugin'; $json['pay_info'] = $result['data']; break; case 'wxapp': //跳转微信小程序支付 $json['pay_type'] = 'wxapp'; $json['pay_info'] = $result['data']; break; case 'error': $json['code'] = -2; $json['msg'] = $result['msg']; break; default: $json['pay_type'] = 'jump'; $json['pay_info'] = $siteurl.'pay/submit/'.TRADE_NO.'/'; break; } if($json['code'] == 0){ if(is_array($json['pay_info'])) $json['pay_info'] = json_encode($json['pay_info']); $json['timestamp'] = time().''; $json['sign_type'] = 'RSA'; $json['sign'] = self::makeSign($json, null); } exit(json_encode($json)); }else{ $json = ['code'=>1, 'trade_no'=>TRADE_NO]; switch($type){ case 'jump': //跳转URL $json['payurl'] = $result['url']; break; case 'html': //显示html跳转 $json['html'] = $result['data']; break; case 'qrcode': //扫码支付 $json['qrcode'] = $result['url']; break; case 'scheme': //小程序H5跳转 $json['urlscheme'] = $result['url']; break; case 'error': $json['code'] = -2; $json['msg'] = $result['msg']; break; default: $json['payurl'] = $siteurl.'pay/submit/'.TRADE_NO.'/'; break; } exit(json_encode($json)); } } // 订单回调处理 static public function processOrder($isnotify, $order, $api_trade_no, $buyer = null, $bill_trade_no = null){ global $DB,$conf,$siteurl; if($order['status']==0 || $order['status']==4){ if($DB->exec("UPDATE `pre_order` SET `status`=1 WHERE `trade_no`='".$order['trade_no']."'")){ $data = ['endtime'=>'NOW()', 'date'=>'CURDATE()']; if(!empty($api_trade_no)) $data['api_trade_no'] = $api_trade_no; if(!empty($buyer)) $data['buyer'] = $buyer; if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no; if($order['settle']>0) $data['settle'] = $order['settle']; $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); $order['api_trade_no'] = $api_trade_no; processOrder($order, $isnotify); } }elseif(empty($order['api_trade_no']) && !empty($api_trade_no)){ $data = ['api_trade_no'=>$api_trade_no]; if(!empty($buyer)) $data['buyer'] = $buyer; if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no; $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); }elseif(empty($order['buyer']) && !empty($buyer)){ $data['buyer'] = $buyer; $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); } if($isnotify && $order['settle']>0){ $DB->update('order', ['settle'=>$order['settle']], ['trade_no'=>$order['trade_no']]); } if(!$isnotify){ include_once SYSTEM_ROOT.'txprotect.php'; if($order['status'] == 2){ $jumpurl = '/payerr.html'; returnTemplate($jumpurl); return; } // 支付完成5分钟后禁止跳转回网站 if(!empty($order['endtime']) && time() - strtotime($order['endtime']) > 300){ $jumpurl = '/payok.html'; }else{ $url=creat_callback($order); $jumpurl = $url['return']; } returnTemplate($jumpurl); } } // 更新订单信息 static public function updateOrder($trade_no, $api_trade_no, $buyer = null, $status = null){ global $DB; $data = ['api_trade_no'=>$api_trade_no]; if(!empty($buyer)) $data['buyer'] = $buyer; if($status) $data['status'] = $status; $DB->update('order', $data, ['trade_no'=>$trade_no]); } // 更新订单扩展信息 static public function updateOrderExt($trade_no, $data){ global $DB; $DB->update('order', ['ext'=>serialize($data)], ['trade_no'=>$trade_no]); } // 更新合单状态 static public function updateOrderCombine($trade_no, $sub_orders = null){ global $DB; $DB->update('order', ['combine'=>1], ['trade_no'=>$trade_no]); if(!empty($sub_orders)){ $DB->delete('suborder', ['trade_no'=>$trade_no]); foreach($sub_orders as $data){ $data['trade_no'] = $trade_no; $data['status'] = 0; $DB->insert('suborder', $data); } } } // 更新订单分账接收人 static public function updateOrderProfits($order, $plugin){ global $DB; $support_plugins = \lib\ProfitSharing\CommUtil::$plugins; if(in_array($plugin, $support_plugins)){ $psreceiver = null; if($order['subchannel'] > 0){ $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `subchannel`='{$order['subchannel']}' AND `status`=1 ORDER BY id ASC LIMIT 1"); } if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `status`=1 ORDER BY id ASC LIMIT 1"); if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid` IS NULL AND `status`=1 ORDER BY id ASC LIMIT 1"); if($psreceiver){ if(!$psreceiver['minmoney'] || $order['realmoney']>=$psreceiver['minmoney']){ $DB->update('order', ['profits'=>$psreceiver['id']], ['trade_no'=>$order['trade_no']]); return intval($psreceiver['id']); } } } return 0; } // 更新订单分账接收人2 static public function updateOrderProfits2($order, $plugin){ return 0; } //支付宝直付通确认结算 public static function alipaydSettle($channel, $order){ $alipay_config = require(PLUGIN_ROOT.'alipayd/inc/config.php'); $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); if($order['combine'] == 1){ $sub_orders = self::getSubOrders($order['trade_no']); if(empty($sub_orders)) throw new Exception('子订单数据不存在'); $failnum = 0; $errmsg = ''; foreach($sub_orders as $sub_order){ if($sub_order['settle'] == 0){ $settle = 0; try{ $alipaySevice->settle_confirm($sub_order['api_trade_no'], $sub_order['money']); $settle = 1; }catch(Exception $e){ if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){ $settle = 1; }else{ $failnum++; $errmsg .= $e->getMessage().','; } } if($settle == 1) self::updateSubOrderSettle($sub_order['sub_trade_no'], 1); } } if($failnum > 0) throw new Exception('部分子单结算失败,失败数量:'.$failnum.',失败原因:'.$errmsg); return true; } try{ $alipaySevice->settle_confirm($order['api_trade_no'], $order['realmoney']); return true; }catch(Exception $e){ if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){ return true; }else{ throw $e; } } } //微信收付通确认结算 public static function wxpaynpSettle($channel, $order){ $wechatpay_config = require(PLUGIN_ROOT.'/wxpaynp/inc/config.php'); if($wechatpay_config['ecommerce']){ if(!$order['profits']){ $client = new \WeChatPay\V3\ProfitsharingService($wechatpay_config); return $client->unfreeze($order['trade_no'], $order['api_trade_no']); }else{ throw new Exception('当前订单需要分账,请进入分账订单页面确认分账'); } }else{ throw new Exception('非电商收付通订单'); } } //支付宝预授权资金支付 public static function alipayPreAuthPay($channel, $order){ global $conf; $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); $trade_no = $order['trade_no']; $bizContent = [ 'out_order_no' => $trade_no, 'out_request_no' => $trade_no, ]; $result = $alipaySevice->preAuthQuery($bizContent); //print_r($result);exit; if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败'); if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0'); if($result['order_status'] == 'AUTHORIZED'){ $auth_no = $result['auth_no']; $ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no):$order['name']; $bizContent = [ 'out_trade_no' => $trade_no, 'total_amount' => $result['rest_amount'], 'subject' => $ordername, 'product_code' => 'PREAUTH_PAY', 'auth_no' => $auth_no, 'auth_confirm_mode' => 'COMPLETE' ]; return $alipaySevice->scanPay($bizContent); }else{ throw new Exception('该笔订单非已授权状态,无需支付'); } } //支付宝预授权资金解冻 public static function alipayUnfreeze($channel, $order){ $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); $trade_no = $order['trade_no']; $bizContent = [ 'out_order_no' => $trade_no, 'out_request_no' => $trade_no, ]; $result = $alipaySevice->preAuthQuery($bizContent); //print_r($result);exit; if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败'); if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0'); if($result['order_status'] == 'AUTHORIZED'){ $auth_no = $result['auth_no']; $bizContent = [ 'auth_no' => $auth_no, 'out_request_no' => date("YmdHis").rand(11111,99999), 'amount' => $result['rest_amount'], 'remark' => '解冻资金' ]; return $alipaySevice->preAuthUnfreeze($bizContent); }else{ throw new Exception('该笔订单非已授权状态,无需解冻'); } } //支付宝红包转账 public static function alipayRedPacketTransfer($channel, $payee_user_id, $money, $order_id){ $out_biz_no = date("YmdHis").rand(11111,99999); $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); $alipaySevice = new \Alipay\AlipayTransferService($alipay_config); $alipaySevice->redPacketTansfer($out_biz_no, $money, $payee_user_id, $conf['sitename'], $order_id); } //支付宝红包资金退回 public static function alipayRedPacketRefund($channel, $trade_no, $money){ $out_biz_no = date("YmdHis").rand(11111,99999); $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); $alipaySevice = new \Alipay\AlipayTransferService($alipay_config); $alipaySevice->redPacketRefund($out_biz_no, $trade_no, $money); } //加锁设置订单扩展数据 public static function lockPayData($trade_no, $func){ global $DB; $DB->beginTransaction(); $data = $DB->getColumn("SELECT ext FROM pre_order WHERE trade_no=:trade_no FOR UPDATE", [':trade_no'=>$trade_no]); if($data) { $DB->rollBack(); return unserialize($data); } try{ $data = $func(); }catch(\Exception $e){ $DB->rollBack(); throw $e; } if($data){ $DB->update('order', ['ext'=>serialize($data)], ['trade_no' => $trade_no]); } $DB->commit(); return $data; } //获取微信客服跳转链接 public static function getWxkfPayUrl($pay_url){ global $order, $DB, $conf; $cookiesid = $_COOKIE['mysid']; if(!$cookiesid||!preg_match('/^[0-9a-z]{32}$/i', $cookiesid)){ $cookiesid = getSid(); setcookie("mysid", $cookiesid, time() + 2592000, '/'); } if($conf['wework_paykfid'] > 0){ $wxkfaccount = $DB->getRow("SELECT * FROM pre_wxkfaccount WHERE id=:id", [':id'=>$conf['wework_paykfid']]); }elseif($conf['wework_paymsgmode'] == 1){ $usekflist = $DB->getAll("SELECT DISTINCT aid FROM pre_wxkflog WHERE `sid`=:sid AND addtime>=:addtime AND status=1", [':sid'=>$cookiesid, ':addtime'=>date("Y-m-d H:i:s", strtotime('-48 hours'))]); $usekfids = [0]; foreach($usekflist as $usekf){ $usekfids[] = intval($usekf['aid']); } $wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE A.id NOT IN (".implode(",", $usekfids).") AND B.status=1 ORDER BY A.usetime ASC LIMIT 1"); }else{ $wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE B.status=1 ORDER BY A.usetime ASC LIMIT 1"); } if(!$wxkfaccount) return false; $DB->insert('wxkflog', ['trade_no'=>$order['trade_no'], 'aid'=>$wxkfaccount['id'], 'sid'=>$cookiesid, 'payurl'=>$pay_url, 'addtime'=>'NOW()']); $scene_param = 'orderid='.$order['trade_no'].'&money='.$order['realmoney']; try{ if(!empty($wxkfaccount['url'])){ $kfurl = $wxkfaccount['url']; $DB->update('wxkfaccount', ['usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]); }else{ $wework = new wechat\WeWorkAPI($wxkfaccount['wid']); $kfurl = $wework->getKFURL($wxkfaccount['openkfid'], 'pay'); $DB->update('wxkfaccount', ['url'=>$kfurl, 'usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]); } $kfurl = 'weixin://biz/ww/kefu/' . $kfurl . '&schema=1'; $kfurl .= '&scene_param='.urlencode($scene_param); return $kfurl; }catch(\Exception $e){ sysmsg($e->getMessage()); } } //支付宝直付通&微信收付通延迟结算处理 public static function settle_task(){ global $DB; $orders = $DB->getAll("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE A.status=1 AND A.settle=1 AND A.addtime 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); if(!$channel) continue; try{ if($row['plugin'] == 'alipayd'){ self::alipaydSettle($channel, $row); }elseif($row['plugin'] == 'wxpaynp'){ self::wxpaynpSettle($channel, $row); } $DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]); echo $trade_no.' 结算成功
'; }catch(Exception $e){ $errmsg = $e->getMessage(); if(strpos($errmsg, 'ALREADY_CONFIRM_SETTLE')){ $DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]); echo $trade_no.' 结算成功
'; continue; } $DB->update('order', ['settle'=>3], ['trade_no'=>$trade_no]); echo $trade_no.' 结算失败,'.$errmsg.'
'; } } } public static function getSubOrders($trade_no){ global $DB; return $DB->getAll("SELECT * FROM pre_suborder WHERE trade_no=:trade_no", [':trade_no'=>$trade_no]); } public static function processSubOrders($trade_no, $sub_orders){ global $DB; foreach($sub_orders as $data){ $DB->update('suborder', ['status'=>1, 'api_trade_no'=>$data['api_trade_no']], ['sub_trade_no'=>$data['sub_trade_no']]); } } public static function refundSubOrder($sub_trade_no, $refundmoney = null){ global $DB; $DB->update('suborder', ['status'=>2, 'refundmoney'=>$refundmoney], ['sub_trade_no'=>$sub_trade_no]); } public static function updateSubOrderSettle($sub_trade_no, $settle){ global $DB; $DB->update('suborder', ['settle'=>$settle], ['sub_trade_no'=>$sub_trade_no]); } }