写了个微信小程序联盟接口api封装,对推广位、联盟商品以及联盟订单的管理提供接口

微信小程序联盟是微信官方的小程序推广后台,

小程序联盟是微信官方提供给小程序商家和推客(推广者)的CPS推广工具。其具有“先成交后付费”的特点,商家在管理后台发布商品推广需求和佣金,佣金在推广者成功完成推广后才会结算。

<?php

namespace wbs\xcx\union;


/**
 * 微信小程序联盟
 * @author blog.alipay168.cn
 * Class UnionXcx
 * @package wbs\xcx\union
 *
 * @notice 注意事项,runtime需要写权限,作为日志和临时数据存放,也可以自定义修改
 */
class UnionXcx
{
    /**
     * @var string 推广位
     */
    private $pid = '';

    /**
     * 接口基础
     * @var string
     */
    private $uri_base = 'https://api.weixin.qq.com';

    /**
     * 登录联盟推客管理后台,点击左侧导航栏里【账号设置】,
     * 获取推客账号appID及appsecret(appsecret需要账号超级管理员微信扫码获取);
     * appid
     * @var string
     */
    private $appid = '';

    /**
     * 登录联盟推客管理后台,点击左侧导航栏里【账号设置】,
     * 获取推客账号appID及appsecret(appsecret需要账号超级管理员微信扫码获取);
     * 小appSecret
     * @var string
     */
    private $secret = '';

    /**
     * 临时存储数据和日志的目录
     * @var string
     */
    public $dir_runtime = '';

    public function __construct($config)
    {
        $this->appid = $config['appid'];
        $this->secret = $config['secret'];
        $this->pid = $config['pid'];
        $this->dir_runtime = !empty($config['runtime']) ? $config['runtime'] : __DIR__ . '/../runtime/';
        if (!is_dir($this->dir_runtime)) {
            @mkdir($this->dir_runtime, 0644, true);
        }

    }

    /**
     * 发起post请求
     * @param $url
     * @param $data array|string
     * @param array $headers
     * @return array|bool|float|int|mixed|\stdClass|string|null
     */
    protected function curl_post($url, $data, $headers = [])
    {
        if (!empty($headers)) {
            $headers = array_merge($headers, ['content-type: application/json']);
        } else {
            $headers = ['content-type: application/json'];
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);//测试时禁用
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);//测试时禁用
        //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        //curl_setopt($curl, CURLOPT_REFERER, $url);
        if (!empty($headers)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        $response = curl_exec($curl);
        $error = curl_error($curl);
        return $error ? $error : $response;
    }

    /**
     * 发起post请求
     * @param $url
     * @param array $data array
     * @param array $headers
     * @return array|bool|float|int|mixed|\stdClass|string|null
     */
    protected function curl_get($url, $data = [], $headers = [])
    {
        if (!empty($data)) {
            if (stripos($url, '?') === false) {
                $url = $url . '?' . http_build_query($data);
            } else {
                $url = $url . '&' . http_build_query($data);
            }
        }
        if (!empty($headers)) {
            $headers = array_merge($headers, ['content-type: application/json']);
        } else {
            $headers = ['content-type: application/json'];
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, 0);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($headers)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        $response = curl_exec($curl);
        $error = curl_error($curl);
        return $error ? $error : $response;
    }

    /**
     * 获取access_token
     * @return string|array
     */
    public function get_access_token()
    {
        $tokenFile = $this->dir_runtime . 'data/access_token';
        if (file_exists($tokenFile)) {
            $info = @json_decode(file_get_contents($tokenFile), true);
            //有效直接返回
            if (!empty($info['expire']) && $info['expire'] > time()) {
                return $info['token'];
            }
        }

        $url = $this->uri_base . '/cgi-bin/token?grant_type=client_credential&appid=' . $this->appid . '&secret=' . $this->secret;


        $res = $this->curl_get($url, '');
        $ret = json_decode($res, true);

        if ((!is_dir($this->dir_runtime) && @mkdir($this->dir_runtime, 0644, true)) || !is_writable($this->dir_runtime)) {
            exit($this->dir_runtime . '目录需要写入权限');
        }
        if (!empty($ret['errcode'])) {
            //日志记录
            if (!is_dir($this->dir_runtime . 'log/')) {
                @mkdir($this->dir_runtime . 'log/', 0644, true);
            }
            @file_put_contents($this->dir_runtime . 'log/' . date('Ymd') . '_err.log', $res . PHP_EOL, FILE_APPEND);
            exit('获取accessToken失败:' . $ret['errmsg']);
            //return ['code' => -1, 'msg' => $ret['errmsg']];
        }
        if (!is_dir($this->dir_runtime . 'data/')) {
            @mkdir($this->dir_runtime . 'data/', 0644, true);
        }
        @file_put_contents($this->dir_runtime . 'data/access_token', json_encode([
            'expire' => time() + 7000,//提前200秒
            'token' => $ret['access_token']
        ]));

        return $ret['access_token'];

    }

    /**
     * 添加推广位
     * @param $name string 推广位名称,如宅家推广
     * @return array
     */
    public function promotion_add($name)
    {
        $data = json_encode([
            'promotionSourceName' => $name
        ], JSON_UNESCAPED_UNICODE);
        $access_token = $this->get_access_token();
        $res = $this->curl_post($this->uri_base . '/union/promoter/promotion/add?access_token=' . $access_token, $data);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return ['code' => 0, 'msg' => 'success', 'pid' => $ret['pid']];
    }


    /**
     * 删除某个推广位
     * @param $pid string 推广位字符串
     * @param $name string 推广位名称
     * @return array
     */
    public function promotion_del($pid, $name)
    {
        $data = json_encode([
            'promotionSourcePid' => $pid,
            'promotionSourceName' => $name,
        ], JSON_UNESCAPED_UNICODE);
        $access_token = $this->get_access_token();
        $res = $this->curl_post($this->uri_base . '/union/promoter/promotion/del?access_token=' . $access_token, $data);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return ['code' => 0, 'msg' => 'success'];
    }


    /**
     * 更新推广位
     * @param $pid string 推广位字符串
     * @param $oldName string 推广位名称(修改前名称)
     * @param $newName string 新推广位名称
     * @return array
     */
    public function promotion_udp($pid, $oldName, $newName)
    {
        $data = json_encode([
            'previousPromotionInfo' => [
                'promotionSourcePid' => $pid,
                'promotionSourceName' => $oldName,
            ],
            'promotionInfo' => [
                'promotionSourceName' => $newName
            ]
        ], JSON_UNESCAPED_UNICODE);
        $access_token = $this->get_access_token();
        $res = $this->curl_post($this->uri_base . '/union/promoter/promotion/upd?access_token=' . $access_token, $data);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return ['code' => 0, 'msg' => 'success'];
    }


    /**
     * 获取推广位列表
     * @param int $start
     * @param int $limit
     * @return array
     */
    public function promotion_list($start = 0, $limit = 20)
    {
        $data = [
            'start' => $start,
            'limit' => $limit
        ];
        $access_token = $this->get_access_token();

        $res = $this->curl_get($this->uri_base . '/union/promoter/promotion/list?access_token=' . $access_token, $data);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return [
            'code' => 0,
            'msg' => 'success',
            'list' => $ret['promotionSourceList'],//推广位数据
            'total' => $ret['total'],//推广位总数
            'limitMaxCount' => $ret['promotionMaxCnt'],//允许创建的推广位最大数量
        ];
    }

    /**
     * 获取联盟商品类目列表及类目ID
     * @return array
     */
    public function product_category()
    {
        $access_token = $this->get_access_token();

        $res = $this->curl_get($this->uri_base . '/union/promoter/product/category?access_token=' . $access_token, '');
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return [
            'code' => 0,
            'msg' => 'success',
            'list' => $ret['productCats'],//类目数据
        ];
    }


    /**
     * 查询商品详情信息
     * 支持根据多种筛选条件获取可供推广的商品列表及详情,筛选条件包括商品关键词(名称、店铺、spuID)、商品价格、商品佣金、佣金比例、是否含有联盟券
     * @param array $args 请求参数
     * @return array
     */
    public function product_list($args = [])
    {
        $param = [
            'from' => !empty($args['page']) ? intval($args['page']) : 1,
            'limit' => !empty($args['limit']) ? intval($args['limit']) : 20,
            //0-搜索商品名称,1-搜索小商店名称;2-搜索商品SPU ID
            'queryType' => isset($args['queryType']) ? intval($args['queryType']) : 0,
        ];
        //商品最高价格,单位分
        if (!empty($args['maxPrice'])) {
            $param['maxPrice'] = intval($args['maxPrice']);
        }
        //商品最低价格,单位分
        if (!empty($args['minPrice'])) {
            $param['minPrice'] = intval($args['minPrice']);
        }
        //佣金金额下限,单位分
        if (!empty($args['minCommissionValue'])) {
            $param['minCommissionValue'] = intval($args['minCommissionValue']);
        }
        //佣金比例下限,单位万分之一
        if (!empty($args['minCommissionRatio'])) {
            $param['minCommissionRatio'] = intval($args['minCommissionRatio']);
        }

        /**排序
         * 0    -默认排序
         * 1    -商品价格升序
         * 2    -商品价格降序
         * 3    -佣金比例升序
         * 4    -佣金比例降序
         * 5    -佣金数值升序
         * 6    -佣金数值降序
         */
        if (isset($args['sortType'])) {
            $param['sortType'] = intval($args['sortType']);
        }
        //类目ID,值来自获取类目列表接口
        if (!empty($args['categoryId'])) {
            $param['categoryId'] = intval($args['categoryId']);
        }
        //是否有联盟券:0-无,1-有
        if (!empty($args['hasCoupon'])) {
            $param['hasCoupon'] = intval($args['hasCoupon']);
        }
        //商品SPUID,多个用英文逗号分隔
        if (!empty($args['productId'])) {
            $param['productId'] = trim($args['productId']);
        }
        //商品类目ID,多个用英文逗号分隔
        if (!empty($args['category'])) {
            $param['category'] = trim($args['category']);
        }
        //黑名单类目ID,不拉出黑名单类目商品,多个用英文逗号分隔
        if (!empty($args['noCategory'])) {
            $param['noCategory'] = trim($args['noCategory']);
        }
        //小商店AppID列表--array
        if (!empty($args['shopAppIds'])) {
            $param['shopAppIds'] = json_encode($args['shopAppIds']);
        }

        $access_token = $this->get_access_token();

        $res = $this->curl_get($this->uri_base . '/union/promoter/product/list?access_token=' . $access_token, $param);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return [
            'code' => 0,
            'msg' => 'success',
            'list' => $ret['productList'],//商品列表数据
            'total' => $ret['total'],//商品总数
        ];
    }

    /**
     * 根据订单ID查询订单详情
     * @param $orderIdList array 订单数组
     * @return array
     */
    public function order_info($orderIdList)
    {
        $data = [
            'orderIdList' => json_encode($orderIdList)
        ];
        $access_token = $this->get_access_token();

        $res = $this->curl_get($this->uri_base . '/union/promoter/order/info?access_token=' . $access_token, $data);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return [
            'code' => 0,
            'msg' => 'success',
            'list' => $ret['orderList'],
        ];
    }

    /**
     * 根据订单支付时间、订单分佣状态拉取订单详情
     * @param array $args
     * @return array
     */
    public function order_search($args = [])
    {
        $param = [
            'page' => !empty($args['page']) ? intval($args['page']) : 1,
        ];
        //起始时间戳,单位为秒
        if (!empty($args['startTimestamp'])) {
            $param['startTimestamp'] = $args['startTimestamp'];
        }
        //结束时间戳,单位为秒
        if (!empty($args['endTimestamp'])) {
            $param['endTimestamp'] = $args['endTimestamp'];
        }

        //分佣状态:SETTLEMENT_PENDING-待结算;SETTLEMENT_SUCCESS-已结算;SETTLEMENT_CANCELED   -取消结算
        if (!empty($args['commissionStatus'])) {
            $param['commissionStatus'] = trim($args['commissionStatus']);
        }

        $access_token = $this->get_access_token();

        $res = $this->curl_get($this->uri_base . '/union/promoter/order/search?access_token=' . $access_token, $param);
        $ret = json_decode($res, true);

        if (!empty($ret['errcode'])) {
            return ['code' => -1, 'msg' => $ret['errmsg']];
        }

        return [
            'code' => 0,
            'msg' => 'success',
            'list' => $ret['orderList'],
            'pageSize' => $ret['pageSize'],
            'totalNum' => $ret['totalNum'],
        ];
    }


}


实例操作:

<?php
require ("../union/UnionXcx.php");

$config = [
    'appid'=>'',
    'secret'=>'',
    'pid'=>'',
    'runtime'=> RUNTIME_PATH.'union_xcx/',
];

$union = new \wbs\xcx\union\UnionXcx($config);

 $data = $union->promotion_list();

 print_r($data );


附件是这次的文件,主要是目录结构:

微信截图_20210430175924.png



评论/留言