<?php
namespace app\api\controller;
use think\Controller;
use app\common\model\ShopInfo as ShopInfoModel;
use app\common\model\UserOrderInfo as UserOrderInfoModel;
use app\common\model\User as UserModel;
use app\common\model\FromInfo as FromInfoModel;
class Pay extends Controller
{
public function _empty()
{
dump(input('get.'));die;
return $this->fetch('/404');
}
//下单
public function unifiedorder() {
$playerid = input('playerid/d'); //玩家id
$shopid = input('shopid/d'); //商品id
$fromid = input('fromid/d'); //渠道号
$orderid = date('ymdHis', time()) . mt_rand(1000, 9999); //商户唯一订单号
$shopinfo = ShopInfoModel::getOne('shopid, shopname, gamecoins, rmb', ['shopid' => $shopid]);
if(empty($shopinfo)) {
return json_return(-1, '商品不存在');
}
if(!$playerid) {
return json_return(-1, '缺少玩家id');
}
//订单信息
$total_fee = (config('environment') == 'test') ? 1 : $shopinfo->rmb * 100;//; //订单金额
$order = [
'appid' => config('system.appid'),
'body' => $shopinfo->shopname,
'mch_id' => config('system.mch_id'),
'nonce_str' => uniqid(),
'notify_url' => config('system.site_url').url('pay/notify'),//接受微信异步通知地址
'out_trade_no' => $orderid,//商户唯一订单号,可包含字母序
'spbill_create_ip'=>request()->ip(),//产生订单号的服务器IP
'total_fee' => $total_fee,//订单金额,单位/分
];
$frominfo = FromInfoModel::getOne('fromid, paytype', ['fromid' => $fromid]);
if(empty($frominfo)) {
return json_return(-1, '渠道号不存在');
}
$pay_type = $frominfo->paytype;
if($fromid < 10000) {//ios
switch ($pay_type) {
case 1:
$order['trade_type'] = 'APP';
$order['appid'] = config('system.ios_appid');
break;
case 2:
$order['trade_type'] = 'MWEB';
$order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}';
break;
case 3:
$order['trade_type'] = 'ApplePay';
$orderRes = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo, 3);
if($orderRes) {
$return_data = [
'trade_type' => 'ApplePay',
'orderid' => $orderid
];
return json_return(0, 'success', $return_data);
}else{
return json_return(-1, '保存订单信息失败');
}
break;
default:
return json_return(-1, '交易类型错误');
break;
}
}else if($fromid > 10000 && $fromid < 100000) { //安卓
switch ($pay_type) {
case 1:
$order['trade_type'] = 'APP';
$order['appid'] = config('system.android_appid');
break;
case 2:
$order['trade_type'] = 'MWEB';
$order['scene_info'] = '{"h5_info": {"type":"Android","app_name": "'.config('system.android_appname').'","package_name": "'.config('system.android_packagename').'"}}';
break;
default:
return json_return(-1, '交易类型错误');
break;
}
}else if($fromid >= 100000) { //other
$order['trade_type'] = 'MWEB';
$order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}';
}else{
return json_return(-1, '当前设备无法支付');
}
$response = $this->getWxUnifiedorderResponse($order);
//若预下单成功,return_code 和result_code为SUCCESS。
if ( $response['return_code'] ==='SUCCESS' && $response['result_code'] ==='SUCCESS') {
//保存订单信息
$res = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo);
if($res) {
//返回trade_type和prepay_id供前端调用
$trade_type = $response['trade_type'];
$return_data = [
'trade_type' => $trade_type,
'orderid' => $orderid
];
switch ($trade_type) {
case 'APP':
$return_data['prepay_order'] = $this->getPrepayOrder($order['appid'], $response['prepay_id']);
break;
case 'MWEB':
$redirect_url = config('system.site_url').'/mweb.php' . '?mweb_url=xxx://xxx'; //后面的mweb_url为浏览器拉起应用的链接
$mweb_url = urlencode($response['mweb_url'].'&redirect_url='.$redirect_url);
$return_data['mweb_url'] = config('system.site_url').'/mweb.php' . '?mweb_url=' .$mweb_url;
//mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
break;
default:
# code...
break;
}
return json_return(0, 'success', $return_data);
}else{
return json_return(-1, '保存订单信息失败');
}
}else{
$errmsg = ($response['return_code'] == 'FAIL') ? $response['return_msg'] : $this->error_code($response['err_code']);
return json_return(-1, $errmsg);
}
}
/**
* [createOrderInfo 创建订单信息]
* @param [integer] $orderid [订单号]
* @param [integer] $playerid [玩家id]
* @param [obj] $shopinfo [商品信息]
* @param integer $paytype [支付类型]
* @return [type] [obj]
*/
private function createOrderInfo($fromid, $orderid, $playerid, $shopinfo, $paytype = 1) {
$userorderinfo = array(
'orderid' => $orderid,
'shopname' => $shopinfo->shopname,
'gamecoins' => $shopinfo->gamecoins,
'rmb' => $shopinfo->rmb,
'orderstatus' => 0,
'fromid' => $fromid,
'paytype' => $paytype, //支付方式1-微信,2-支付宝,3-Apple Pay
'playerid' => $playerid,
'shopid' => $shopinfo->shopid,
'orderdate' => date('Y-m-d H:i:s'),
);
return UserOrderInfoModel::create($userorderinfo);
}
/**
* [getWxUnifiedorderResponse 获得微信下单结果]
* @param [array] $orderid [订单信息]
* @return [array]
*/
private function getWxUnifiedorderResponse($order) {
$sign = $this->getSign($order);
$order['sign'] = $sign;
//转换成一维XML格式
$xml = '<xml>';
foreach($order as $k=>$v){
$xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>';
}
$xml.='</xml>';
//CURL会话
$response = curlHtml('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);
//将xml格式的$response 转成数组
$response = json_decode( json_encode( simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA) ), true );
return $response;
}
//执行第二次签名,才能返回给客户端使用
private function getPrepayOrder($appid, $prepayid){
$data["appid"] = $appid;
$data["noncestr"] = uniqid();
$data["package"] = "Sign=WXPay";
$data["partnerid"] = config('system.mch_id');
$data["prepayid"] = $prepayid; //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
$data["timestamp"] = time();
$data["sign"] = $this->getSign($data);
return $data;
}
/**
* 格式化参数格式化成url参数
*/
private function ToUrlParams($data)
{
$buff = "";
foreach ($data as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
public function getSign($data)
{
//签名步骤一:按字典序排序参数
if(is_array($data)){
ksort($data);
} else{
$data = [];
}
$string = $this->ToUrlParams($data);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".config('system.apikey');
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
//订单查询
public function order_query() {
$orderid = input('orderid');
require_once "./pay/wxpay/lib/WxPay.Api.php";
$input = new \WxPayOrderQuery();
$input->SetOut_trade_no($orderid);
$result = \WxPayApi::orderQuery($input);
if ($result['err_code_des'] == "order not exist") {
// 订单不存在
$trade_state = '订单不存在';
} else {
if ($result['trade_state'] == "SUCCESS") {
$trade_state = '支付成功'; //支付成功
return json_return(0, $trade_state, $result);
} else if ($result['trade_state'] == "REFUND") {
$trade_state = '已退款'; //已退款
} else if ($result['trade_state'] == "NOTPAY") {
$trade_state = '未支付'; //用户还没支付
} else if ($result['trade_state'] == "CLOSED") {
$trade_state = '订单关闭'; //订单关闭
} else if ($result['trade_state'] == "REVOKED") {
$trade_state = '已撤销'; //已撤销(刷卡支付)
} else if ($result['trade_state'] == "USERPAYING") {
$trade_state = '支付中'; //用户支付中
} else if ($result['trade_state'] == "PAYERROR") {
$trade_state = '支付失败'; //支付失败(其他原因,例如银行返回失败)
}
}
return json_return(-1, $trade_state);
}
//支付完成后的回调
public function notify()
{
$xml = file_get_contents('php://input');
$result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
//为了防止假数据,验证签名是否和返回的一样。
$sign = $this->getSign($result);
if ( $sign === $result['sign']) {
// 校验返回的订单金额是否与商户侧的订单金额一致。修改订单表中的支付状态。
if ($result['result_code'] == "SUCCESS" && $result['return_code'] == "SUCCESS") {
//获取订单信息
$orderinfo = UserOrderInfoModel::getOne('orderid, rmb, playerid,shopid,orderstatus', ['orderid' => $result['out_trade_no']]);
$total_fee = (config('environment') == 'test') ? 1 : $orderinfo->rmb * 100;//; //订单金额
if (($total_fee == $result['total_fee']) && ($orderinfo['orderstatus'] == 0)) {
$this->payHandler($orderinfo);
}
}
}
$return = ['return_code'=>'SUCCESS','return_msg'=>'OK'];
$xml = '<xml>';
foreach($return as $k=>$v){
$xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>';
}
$xml.='</xml>';
echo $xml;
}
//支付成功后的处理
private function payHandler($orderinfo) {
UserOrderInfoModel::transaction(function() use($orderinfo){ //开启事务
//更新订单等操作
});
}
/**
* 错误代码
* @param $code 服务器输出的错误代码
* return string
*/
private function error_code($code)
{
$errList = array(
'NOAUTH' => '商户未开通此接口权限',
'NOTENOUGH' => '用户帐号余额不足',
'ORDERNOTEXIST' => '订单号不存在',
'ORDERPAID' => '商户订单已支付,无需重复操作',
'ORDERCLOSED' => '当前订单已关闭,无法支付',
'SYSTEMERROR' => '系统错误!系统超时',
'APPID_NOT_EXIST' => '参数中缺少APPID',
'MCHID_NOT_EXIST' => '参数中缺少MCHID',
'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配',
'LACK_PARAMS' => '缺少必要的请求参数',
'OUT_TRADE_NO_USED' => '同一笔交易不能多次提交',
'SIGNERROR' => '参数签名结果不正确',
'XML_FORMAT_ERROR' => 'XML格式错误',
'REQUIRE_POST_METHOD' => '未使用post传递参数 ',
'POST_DATA_EMPTY' => 'post数据不能为空',
'NOT_UTF8' => '未使用指定编码格式',
);
if (array_key_exists($code, $errList)) {
return $errList[$code];
}else{
return $code;
}
}
}