Files
amb_wechatapp/extend/douyin/Byte.php
T

548 lines
17 KiB
PHP

<?php
namespace douyin;
class Byte
{
private $orderParam;
private $app_id;
private $secret;
private $merchant_id;
private $salt;
private $valid_time;
private $notify_url;
private $settle_url;
private $token;
private $rsa_private_key;
private $key_version;
private $codeUrl = 'https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session?';
private $tokenUrl = 'https://developer.toutiao.com/api/apps/v2/token';
private $clientTokenUrl = 'https://open.douyin.com/oauth/client_token/';
private $orderQueryUrl = 'https://open.douyin.com/api/trade_basic/v1/developer/order_query/';
protected $payUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_order';
protected $query = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_order';
protected $pushUrl = 'https://developer.toutiao.com/api/apps/order/v2/push';
// protected $qrcodeUrl = 'https://open.douyin.com/api/apps/v1/qrcode/create/';
protected $qrcodeUrl = 'https://developer.toutiao.com/api/apps/qrcode';
// protected $qrcodeUrl = 'https://open-sandbox.douyin.com/api/apps/qrcode';
protected $refundCreateUrl = 'https://open.douyin.com/api/trade_basic/v1/developer/refund_create/';
protected $refundQueryUrl = 'https://open.douyin.com/api/trade_basic/v1/developer/refund_query/';
protected $settle = 'https://developer.toutiao.com/api/apps/ecpay/v1/settle';
protected $sendMsgUrl = 'https://developer.toutiao.com/api/apps/subscribe_notification/developer/v1/notify';
private $notifyOrder;
public static function init($config)
{
if (empty($config['app_id'])) {
throw new \Exception('not empty app_id');
}
if (empty($config['secret'])) {
throw new \Exception('not empty secret');
}
$class = new self();
$class->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;
}
}