最近开发了一个抖音小程序,在订单推送到抖音订单中心接口对接过程中折腾了半天,最终在朋友的协助下找到了问题。为了避免再次走弯路,特将本次集成的抖音小程服务器端API对接文件记录一下。
抖音小程序服务器端API对接源码:
- <?php
- namespace app\api\logic;
- use think\facade\Db;
- use think\facade\Cache;
- use service\HttpService;
- /**
- * PaymentLogic.php
- * Created by 繁华如梦.
- * Email: [email protected]
- * DateTime: 2022/3/3 10:09
- */
- class PaymentLogic{
- public static $salt = "VTGViUk3************aujxZ7uQp";
- public static $merch_id = "7044*********7650";
- public static $appid = "ttd76d********aa01";
- public static $secret = "8e09bac***********1ef758d9d";
- public static $token = "EfFyTSwvRj************bWfYELduNb"; //开放平台设置
- public static $notify = "https://************ment/dy_notify"; //默认回调通知
- public static $url = "https://developer.toutiao.com/api/apps/ecpay/v1/";
- /**
- * 抖音下单
- * @param $data array 订单信息
- * $data=['out_order_no'=>$this->order_number(),'total_amount'=>1,//单位:分'subject'=>'测试商111品','body'=>'测试详情','valid_time'=>7200,];
- */
- public static function order($data){
- $result = self::post('create_order',$data);
- return $result;
- }
- /**
- * 抖音订单查询
- * @param $out_trade_no string 交易单号
- * @return mixed
- */
- public static function query($out_trade_no){
- $data = ['out_order_no'=>$out_trade_no];
- $result = self::post('query_order',$data,false);
- return $result;
- }
- /**
- * 订单退款
- * @param $data array 订单信息
- * $data=['out_order_no'=>'2021110118351347832','out_refund_no'=>$this->order_number(),'reason'=>'退款原因','refund_amount'=>1,];
- */
- public static function refund($data){
- $result = self::post('create_refund',$data);
- return $result;
- }
- /**
- * 订单分账
- * @param $data array 订单信息
- *
- */
- public static function settle($data){
- /**
- $data=[
- 'out_order_no'=>'2021110118301265990',
- 'out_settle_no'=>$this->order_number(),
- 'settle_desc'=>'分账描述',
- 'settle_params'=>json_encode([]),//分润方参数 如[['merchant_uid'=>'商户号','amount'=>'10']] 可以有多个分账商户
- ];
- */
- $result = self::post('settle',$data);
- return $result;
- }
- /**
- * 订单推送到抖音
- * @param $data array 订单数据
- * @note order_status 与 status须保持一致,但类型不同
- * @return array
- */
- public static function pushOrder($data){
- $api = "https://developer.toutiao.com/api/apps/order/v2/push";
- $openid = Db::name('dy_user')->where(['id'=>$data['uid']])->value('openid');
- $product = Db::name('product')->where(['id'=>$data['product_id']])->field('id,cover_img')->find();
- $course = Db::name('course')->where(['id'=>$data['course_id']])->value('name');
- $item_list = [['img'=>$product['cover_img'],
- 'title'=>$course,'sub_title'=>$course,'amount'=>1,'price'=>$data['price']*100]];
- $detail = [
- 'order_id'=>(string)$data['id'],'create_time'=>$data['create_time']*1000,'status'=>"已支付",'amount'=>1,
- 'total_price'=>$data['amount']*100,'detail_url'=>"pages/course/course",'item_list'=>$item_list];
- $param = ['access_token'=>self::getAccessToken(),'app_name'=>"douyin",
- 'open_id'=>$openid,'update_time'=>self::getMillisecond(),'order_detail'=>json_encode($detail),'order_type'=>0,'order_status'=>1,'payment_order_no'=>emptyempty($data['third_order_num'])?$data['sn']:$data['third_order_num']];
- $result = HttpService::post($api,json_encode($param),30,['Content-Type: application/json']);
- $data = json_decode($result,true);
- if($data['err_code'] == 0){
- return ['code'=>200,'msg'=>$data['err_msg'],'data'=>$result];
- }else{
- return ['code'=>500,'msg'=>'err_code:'.$data['err_code'].'msg:'.$data['err_msg'],'data'=>[]];
- }
- }
- /**
- * 获取AccessToken
- */
- private static function getAccessToken(){
- $api = "https://developer.toutiao.com/api/apps/v2/token";
- $param = ['appid'=>self::$appid,'secret'=>self::$secret,'grant_type'=>"client_credential"];
- $access_token = Cache::get('dy_accessToken');
- if(emptyempty($access_token)){
- $result = HttpService::post($api,json_encode($param),30,['Content-Type: application/json']);
- $data = json_decode($result,true);
- if($data['err_no'] == 0){
- $access_token = $data['data']['access_token'];
- Cache::set('dy_accessToken',$access_token,$data['data']['expires_in']);
- }
- }
- return $access_token;
- }
- /**
- * 请求小程序平台服务端
- * @param string $url 接口地址
- * @param array $data 参数内容
- * @param boolean $notify 是否有回调
- * @return array
- */
- private static function post($method,$data,$notify=true){
- $data['app_id']=self::$appid;
- if(!emptyempty($notify)){
- $data['notify_url']= self::$notify;
- }
- $data['sign']= self::sign($data);
- $url=self::$url.$method;
- $res = HttpService::post($url,json_encode($data),30,['Content-Type: application/json']);
- return json_decode($res,true);
- }
- /**
- * 签名算法
- * @param $map
- * @return string
- */
- private static 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,self::$salt);
- sort($rList, 2);
- return md5(implode('&', $rList));
- }
- /**
- * 回调验签
- * @param array $map 验签参数
- * @return stirng
- */
- public static function handler($map){
- $rList = array();
- array_push($rList, self::$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));
- }
- /**
- * 成功返回
- */
- public static function success(){
- return json_encode(['err_no'=>0,'err_tips'=>"success"]);
- }
- /**
- * 写日志
- * @param string $path 日志路径
- * @param string $content 内容
- */
- public static function log($path, $content){
- $file = fopen($path, "a");
- fwrite($file, date('Y-m-d H:i:s').'-----'.$content."\n");
- fclose($file);
- }
- private static function getMillisecond() {
- list($t1, $t2) = explode(' ', microtime());
- return (float)sprintf('%.0f',(floatval($t1)+floatval($t2))*1000);
- }
- }
温馨提示:
1、上述代码中用到的HttpService是基于curl对get和post的二次封装,源码详见https://gitee.com/zkii_admin/Tp-admin中“/extend/service/HttpService.php”。
2、推送系统订单到抖音订单中心的方法,传入系统内订单数据,在重新组合后,发起请求即可。注意order_detail的数据格式。这里一不小心很容易出错。调试的时候,可以在curl上获取响应头以便查看具体的错误信息。上述代码已经过测试。
3、以上代码基于Thinkphp6编写,在其他框架使用时需要自行修改。