app_id = $config['app_id']; $class->secret = $config['secret']; if (!empty($config['merchant_id'])) { $class->merchant_id = $config['merchant_id']; } if (!empty($config['salt'])) { $class->salt = $config['salt']; } if (!empty($config['token'])) { $class->token = $config['token']; } if (!empty($config['rsa_private_key'])) { $class->rsa_private_key = $config['rsa_private_key']; } if (!empty($config['key_version'])) { $class->key_version = $config['key_version']; } if (!empty($config['notify_url'])) { $class->settle_url = isset($config['settle_url']) ? $config['settle_url'] : $config['notify_url']; $class->notify_url = $config['notify_url']; } $class->valid_time = isset($config['valid_time']) ? $config['valid_time'] : time() + 900; return $class; } /** * 获取下单信息 */ public function getParam() { return $this->orderParam; } /** * 获取异步订单信息 */ public function getNotifyOrder() { $data = file_get_contents("php://input"); $order = json_decode($data, true); $order['msg'] = json_decode($order['msg'], true); $this->notifyOrder = $order; return $this->notifyOrder; } /** * 设置订单号 金额 描述 * @param string $rder_no 平台订单号 * @param int $money 订单金额 * @param string $title 描述 * */ public function set($order_no, $money, $title, $desc = '') { $orderParam["out_order_no"] = $order_no; $orderParam["total_amount"] = $money; $orderParam["subject"] = $title; $orderParam["body"] = $desc; $orderParam["notify_url"] = $this->notify_url; $orderParam["valid_time"] = 7200; $orderParam["store_uid"] = $this->merchant_id; $orderParam["app_id"] = $this->app_id; $data = json_encode(["sign" => $this->sign($orderParam)] + $orderParam); $this->orderParam = json_decode($this->curl_post($this->payUrl, $data), true); return $this; } /** * 获取token */ public function orderQuery($out_order_no) { $tokenData = $this->getClientToken(); if(!$tokenData || !isset($tokenData['message']) || $tokenData['message'] !== 'success'){ return false; } $token = $tokenData['data']['access_token']; $header = ['access-token:'.$token,'Content-Type:'.'application/json']; return json_decode($this->curl_post($this->orderQueryUrl, json_encode([ 'out_order_no' => $out_order_no ]) ,$header), true); } /** * 通用交易系统 * 获取支付参数 * @param $data * @return mixed */ public function getByteAuthorization($data){ return (new \douyin\Order())->getByteAuthorization($data,$this->app_id,$this->rsa_private_key,$this->key_version); } /** * @param array $map * @return string */ public function sign($map) { $rList = array(); foreach ($map as $k => $v) { if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id") { continue; } $value = trim(strval($v)); $len = strlen($value); if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"") { $value = substr($value, 1, $len - 1); } $value = trim($value); if ($value == "" || $value == "null") { continue; } array_push($rList, $value); } array_push($rList, $this->salt); sort($rList, 2); return md5(implode('&', $rList)); } /** * 担保支付同步订单 * @param $openId * @param $orderDetail * @return false|mixed */ public function pushOrder($openId,$orderDetail){ $tokenData = $this->getToken(); if(!$tokenData || !isset($tokenData['err_no']) || $tokenData['err_no'] !== 0){ return false; } $token = $tokenData['data']['access_token']; $data = [ 'access_token'=>$token,//服务端 API 调用标识,通过 getAccessToken 获取 'app_name'=>'douyin',//做订单展示的字节系 app 名称 'open_id'=>$openId,//小程序用户的 open_id,通过 code2Session 获取 'order_type'=>0,//订单类型 'order_status'=>1,//普通小程序订单订单状态 'update_time'=>time(),//订单信息变更时间,10 位秒级时间戳 'order_detail'=>json_encode($orderDetail) ]; $res = $this->curl_post($this->pushUrl, json_encode($data)); return json_decode($res, true); } /** * 获取小程序码 小程序/小游戏启动参数,小程序则格式为 encode({path}?{query}) * @param $path * @return false|mixed */ public function qrcode($path){ $tokenData = $this->getToken(); // dump($tokenData); // if(!$tokenData || !isset($tokenData['message']) || $tokenData['message'] !== 'success'){ // return false; // } if(!$tokenData || !isset($tokenData['err_no']) || $tokenData['err_no'] !== 0){ return false; } $token = $tokenData['data']['access_token']; $data = [ 'access_token'=>$token, 'path'=>urlencode($path), 'appname'=>'douyin' ]; $res = $this->curl_post($this->qrcodeUrl, json_encode($data)); return $res; } /** * 获取token */ public function getToken() { $arr = [ 'grant_type' => 'client_credential', 'appid' => $this->app_id, 'secret' => $this->secret, ]; return json_decode($this->curl_post($this->tokenUrl, json_encode($arr)), true); } /** * 生成 client_token * 该接口用于获取接口调用的凭证 client_token。该接口适用于抖音授权。 */ public function getClientToken() { $arr = [ 'grant_type' => 'client_credential', 'client_key' => $this->app_id, 'client_secret' => $this->secret ]; return json_decode($this->curl_post($this->clientTokenUrl, $arr,[ 'Content-Type'=>'multipart/form-data' ]), true); } /** * 获取 openid * * @param string $code * @param string $anonymous_code * @return void * @author LiJie */ public function getOpenid($code, $anonymous_code = "") { $url = $this->codeUrl . "appid=" . $this->app_id . "&secret=" . $this->secret . "&code=" . $code; if ($anonymous_code) { $url .= "&anonymous_code=" . $anonymous_code; } return json_decode($this->curl_get($url), true); } /** * 异步回调 * @param $order 回调数据 * @return bool true 验签通过|false 验签不通过 */ public function notifyCheck() { $order = $this->getNotifyOrder(); $data = [ $order['timestamp'], $order['nonce'], json_encode($order['msg']), $this->token, ]; sort($data, SORT_STRING); $str = implode('', $data); if (!strcmp(sha1($str), $order['msg_signature'])) { return true; } return false; } /** * 担保支付验签 * 回调验签 * @param array $map 验签参数 * @return stirng */ // public function checkSign($map){ // $rList = array(); // array_push($rList, $this->token); // foreach($map as $k =>$v) { // if ( $k == "type" || $k=='msg_signature') // continue; // $value = trim(strval($v)); // if ($value == "" || $value == "null") // continue; // array_push($rList, $value); // } // sort($rList,2); // return sha1(implode($rList)); // } // /** * 通用交易系统验签 * @param $publicKey * @return bool */ public function verifySignature($publicKey) { $reqInfo = $this->resolveReq(); $message = $reqInfo['timestamp'] . "\n" . $reqInfo['nonce'] . "\n" . $reqInfo['body'] . "\n"; $publicKey = openssl_get_publickey($publicKey); $result = openssl_verify($message, base64_decode($reqInfo['signature']), $publicKey, OPENSSL_ALGO_SHA256); return $result === 1; } public function resolveReq() { $body = $this->getRequestBodyParamsStr(); $timestamp = $_SERVER['HTTP_BYTE_TIMESTAMP']; $nonce = $_SERVER['HTTP_BYTE_NONCE_STR']; $signature = $_SERVER['HTTP_BYTE_SIGNATURE']; $reqInfo = [ 'body'=>$body, 'nonce'=>$nonce, 'timestamp'=>$timestamp, 'signature'=>$signature ]; return $reqInfo; } public function getRequestBodyParamsStr() { $input = file_get_contents('php://input'); $input = str_replace(' ', '', $input); return $input; } /** * 申请退款 * */ public function applyOrderRefund() { $order = []; $order['order_id'] = 'N7301891140469672233';//交易系统侧订单号,长度 <= 64 byte $order['out_refund_no'] = '202311065796282978001400';//开发者侧退款单号,长度 <= 64 byte $order['order_entry_schema'] = '';//退款单的跳转的 schema $order['notify_url'] = '';//退款结果通知地址,必须是 HTTPS 类型 $order['refund_reason'] = json_encode(['退款']);//退款原因,可填多个,不超过10个 $order['refund_total_amount'] = 1;//退款总金额,单位[分] return json_decode($this->curl_post($this->refundCreateUrl, json_encode(['sign' => $this->sign($order)] + $order)), true); } /** * 订单查询 * @param string $out_order_no 订单号 * @return array 订单信息 */ public function findOrder($out_order_no) { if (empty($out_order_no)) { return false; } $order = [ 'out_order_no' => $out_order_no, 'app_id' => $this->app_id, ]; $order['sign'] = $this->sign($order); return json_decode($this->curl_post($this->query, json_encode($order)), true); } /** * 分账 * * @param [type] $order * @return void * @author LiJie */ public function settle($order) { $data = [ 'app_id' => $this->app_id, 'out_settle_no' => $order['out_settle_no'], 'out_order_no' => $order['out_order_no'], 'settle_desc' => $order['settle_desc'], 'notify_url' => $this->settle_url, 'cp_extra' => $order['cp_extra'], ]; $data['sign'] = $this->sign($data); $result = json_decode($this->curl_post($this->settle, json_encode($data)), true); return $result; } /** * 发送模版消息 * * @param [type] $data * @param [type] $token */ public function sendMsg($data, $token) { $data['access_token'] = $token; $data['app_id'] = $this->app_id; return json_decode($this->curl_post($this->sendMsgUrl, json_encode($data)), true); } /** * 解密手机号 * * @param string $session_key 前端传递的session_key * @param string $iv 前端传递的iv * @param string $encryptedData 前端传递的encryptedData */ public function decryptPhone($session_key, $iv, $encryptedData) { if (strlen($session_key) != 24) { return false; } $aesKey = base64_decode($session_key); if (strlen($iv) != 24) { return false; } $aesIV = base64_decode($iv); $aesCipher = base64_decode($encryptedData); $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV); $dataObj = json_decode($result); if ($dataObj == null) { return false; } if ($dataObj->watermark->appid != $this->app_id) { return false; } return json_decode($result, true); } protected static function curl_get($url) { $headerArr = array("Content-type:application/x-www-form-urlencoded"); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); $output = curl_exec($ch); if (!$output) { throw new \Exception(curl_errno($ch)); } curl_close($ch); return $output; } /** * 发起退款 * @param $path * @return false|mixed */ public function refund($params){ $tokenData = $this->getClientToken(); if(!$tokenData || !isset($tokenData['message']) || $tokenData['message'] !== 'success'){ return false; } $token = $tokenData['data']['access_token']; $header = ['access-token:'.$token,'Content-Type:'.'application/json']; return json_decode($this->curl_post($this->refundCreateUrl, json_encode($params) ,$header), true); } /** * 查询退款 * @param $out_refund_no 开发者系统生成的退款单号,长度 <= 64byte * @document https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/trade-system/general/refund/query_refund * @return false|mixed */ public function refundQuery($out_refund_no){ $tokenData = $this->getClientToken(); if(!$tokenData || !isset($tokenData['message']) || $tokenData['message'] !== 'success'){ return false; } $token = $tokenData['data']['access_token']; $header = ['access-token:'.$token,'Content-Type:'.'application/json']; return json_decode($this->curl_post($this->refundQueryUrl, json_encode([ 'refund_id'=>$out_refund_no ]) ,$header), true); } /** * @desc post 用于退款 */ protected static function curl_post($url, $data,$header=[]) { $requestHeader = array( 'Content-Type: application/json', ); if(is_string($data)){ $requestHeader[] = 'Content-Length: '. strlen($data); } if($header){ $requestHeader = $header; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($ch, CURLOPT_HTTPHEADER,$requestHeader); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $output = curl_exec($ch); if (!$output) { throw new \Exception(curl_errno($ch)); } curl_close($ch); return $output; } }