<?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;
}
}
}