PHP Thinkphp5.0实现抖音团购券码核销

文章承接上一篇,讲述如何实现抖音团购券码的核销功能。因项目需要,作者直接给出相关代码,称代码能实现核销,具体业务需自行编写,还提醒有需要的小伙伴关注、收藏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章我们说到项目里需要用到美团API接口来实现卡券核销的功能。
那么这篇文章就讲一下,如何实现抖音团购券码的核销功能。
近期项目需要,抖音团购的券码核销,话不多说,直接上代码吧!
凑合着用吧,反正能销,具体业务需要自己写

<?php

/*
 * 抖音店铺团购券核销
 * author: Mr. YuYang
 *
 */
class DouyinTest
{
    protected $Appid = '';//appid
    protected $AppSecret = '';//秘钥
    protected $pro_id = ''; // 门店id

    /*
     *  这里是核销的方法
     */
    public function DouyinAfterVerification(){
        $encrypted_data = '100000000000000';   // code  格式 100000000000000
        $url = 'https://open.douyin.com/goodlife/v1/fulfilment/certificate/prepare/';
        $client_token = $this->getClientToken();
        if(!$client_token){
            die('token 获取失败');
        }
        $res = $this->prepareCertificate($client_token,'',$encrypted_data);
        $v = $res['data']['verify_token'];
        $code = $res['data']['certificates'][0]['encrypted_code'];
        $hexiao = $this->verifyCoupon($v,$client_token,$this->pro_id,[$code]);
        //核销成功后的所有信息都在这里 打印自己看就行
        //所有的逻辑根据自己项目需要来自己写。
        print_r($hexiao);
    }

    /**
     * @param $access_token
     * @param $encrypted_data
     * @param null $code
     * @return mixed
     * 通过code 核销券码准备
     */
    public function prepareCertificate($access_token, $encrypted_data, $code = null) {
        $url = 'https://open.douyin.com/goodlife/v1/fulfilment/certificate/prepare/';

        $params = [];
        if (!empty($encrypted_data)) {
            $params['encrypted_data'] = urlencode($encrypted_data);
        } elseif (!empty($code)) {
            $params['code'] = $code;
        } else {
            throw new Exception("encrypted_data 和 code 参数不能同时为空");
        }
        $back = $this->douyinRequest($url,$params,$access_token,0);
        return $back;
    }

    /**
     * @param $verifyToken
     * @param $client_token
     * @param $poiId
     * @param $encryptedCodes
     * @param null $codes
     * @param null $orderId
     * @param null $codeWithTimeList
     * @param null $voucher
     * @return mixed
     */
    public function verifyCoupon($verifyToken,$client_token, $poiId, $encryptedCodes, $codes = null, $orderId = null, $codeWithTimeList = null, $voucher = null) {
        $url = 'https://open.douyin.com/goodlife/v1/fulfilment/certificate/verify/';
        $data = array(
            'verify_token' => $verifyToken,
            'poi_id' => $poiId,
            'encrypted_codes' => $encryptedCodes,
            'codes' => $codes,
        );

        $response = $this->douyinRequest($url,$data,$client_token);
        return $response;
    }

    /**
     * @return mixed
     * 获取token
     */
    public function getClientToken(){
        //正式环境:https://open.douyin.com/oauth/client_token/
        //沙盒环境:https://open-sandbox.douyin.com/oauth/client_token/
        $url = 'https://open.douyin.com/oauth/client_token/';
        $param = [
            'client_key' => $this->Appid,
            'client_secret' => $this->AppSecret,
            'grant_type' => 'client_credential'
        ];

        $res = $this->requestUrl($url,$param);
        $back = json_decode($res,true);
        if($back['data']['error_code'] == 0){
            return $back['data']['access_token'];
        }

    }

    /** curl 提交*/
    public function requestUrl($url,$data=null,$https=true,$method='post'){
        //1.初始化url
        $ch = curl_init($url);
        //2.设置相关的参数
        //字符串不直接输出,进行一个变量的存储
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        //判断是否为https请求
        if($https === true){
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        //判断是否为post请求
        if($method == 'post'){
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:multipart/form-data'));
        //3.发送请求
        $str = curl_exec($ch);
        //4.关闭连接
        curl_close($ch);
        //6.返回请求到的结果
        return $str;
    }


    /**
     * 抖音请求方法
     * @param $url
     * @param $client_token
     * @param array $data
     * @param int $is_post
     * @return mixed
     */
    public function douyinRequest($url,$data = [],$client_token = '',$is_post = 1){
        $headers = [
            'Content-Type: application/json',
            'access-token: ' . $client_token,
        ];
        $curl = curl_init();
        if($is_post){
            curl_setopt_array($curl, array(
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_ENCODING => '',
                CURLOPT_CUSTOMREQUEST => 'POST',
                CURLOPT_POSTFIELDS => json_encode($data),
                CURLOPT_HTTPHEADER => $headers,
            ));
        }else{
            $data = http_build_query($data);
            curl_setopt_array($curl, [
                CURLOPT_URL => "$url?$data",
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTPHEADER => $headers,
            ]);
        }
        $response = curl_exec($curl);
        curl_close($curl);
        return json_decode($response,true);
    }
}

以上的代码是抖音卡券核销的功能。有需要的小伙伴记得关注、收藏。

<think>由于用户的问题涉及UniApp开发小程序与ThinkPHP5.0后端框架结合实现获取用户手机号功能,我将分步骤进行说明。 在小程序中,获取用户手机号需要经过以下步骤: 1. 前端(UniApp)调用小程序的API获取手机号加密数据。 2. 前端将加密数据发送到后端(ThinkPHP5.0)。 3. 后端使用session_key和appid等参数对加密数据进行解密,得到手机号。 注意:小程序的手机号获取接口需要用户主动触发(如点击按钮),且需要先通过`tt.login`接口获取登录凭证(code),然后通过后端换取session_key。 具体实现步骤: ### 一、UniApp前端代实现 在UniApp中,我们可以使用`@click`事件触发获取手机号的操作。小程序提供了`getPhoneNumber`事件来获取加密的手机号数据。 1. 在页面中放置一个按钮,并设置`open-type="get_phone_number"`,同时绑定`@getphonenumber`事件。 ```vue <button open-type="get_phone_number" @getphonenumber="getPhoneNumber">获取手机号</button> ``` 2. 在`methods`中定义`getPhoneNumber`方法: ```javascript methods: { async getPhoneNumber(e) { // 注意:小程序返回的是加密数据,需要后端解密 if (e.detail.errMsg === 'getPhoneNumber:ok') { // 获取到加密数据 const encryptedData = e.detail.encryptedData; const iv = e.detail.iv; // 先调用登录接口获取code,用于后端换取session_key uni.login({ provider: 'toutiao', success: async (loginRes) => { const code = loginRes.code; // 将code、encryptedData、iv发送到后端 uni.request({ url: 'https://yourdomain.com/api/decryptPhone', // 替换为你的后端接口 method: 'POST', data: { code: code, encryptedData: encryptedData, iv: iv }, success: (res) => { if (res.data.code === 200) { // 解密成功,获取手机号 const phoneNumber = res.data.phoneNumber; console.log(phoneNumber); uni.showToast({ title: '手机号:' + phoneNumber, icon: 'none' }); } else { uni.showToast({ title: res.data.msg, icon: 'none' }); } } }); } }); } else { // 用户拒绝了授权 uni.showToast({ title: '用户拒绝了授权', icon: 'none' }); } } } ``` ### 二、ThinkPHP5.0后端实现 后端需要提供一个接口,接收前端传来的`code`、`encryptedData`和`iv`,然后通过小程序的接口用`code`换取`session_key`,再用`session_key`解密手机号。 1.ThinkPHP中创建一个控制器,例如`DecryptController.php`。 2. 编写一个方法,用于解密手机号。 首先,需要配置小程序的appid和appsecret(在开放平台获取)。 ```php <?php namespace app\api\controller; use think\Controller; use think\Request; class DecryptController extends Controller { // 小程序的appid和appsecret private $appid = '你的appid'; private $appsecret = '你的appsecret'; public function decryptPhone() { $request = Request::instance(); $code = $request->post('code'); $encryptedData = $request->post('encryptedData'); $iv = $request->post('iv'); if (empty($code) || empty($encryptedData) || empty($iv)) { return json(['code' => 400, 'msg' => '参数缺失']); } // 1. 使用code换取session_key $sessionKey = $this->getSessionKey($code); if ($sessionKey === false) { return json(['code' => 401, 'msg' => '获取session_key失败']); } // 2. 解密数据 $phoneData = $this->decryptData($encryptedData, $iv, $sessionKey); if ($phoneData === false) { return json(['code' => 402, 'msg' => '解密失败']); } // 3. 返回手机号 $phoneNumber = $phoneData['phoneNumber']; return json(['code' => 200, 'msg' => '成功', 'phoneNumber' => $phoneNumber]); } /** * 通过code获取session_key * @param $code * @return bool|mixed */ private function getSessionKey($code) { $url = "https://developer.toutiao.com/api/apps/jscode2session?appid={$this->appid}&secret={$this->appsecret}&code={$code}"; $res = $this->httpGet($url); $data = json_decode($res, true); if (isset($data['session_key'])) { return $data['session_key']; } return false; } /** * 解密数据 * @param $encryptedData * @param $iv * @param $sessionKey * @return bool|mixed */ private function decryptData($encryptedData, $iv, $sessionKey) { require_once 'path/to/wxBizDataCrypt.php'; // 这里需要引入解密类,小程序解密算法与微信相同 $pc = new \WXBizDataCrypt($this->appid, $sessionKey); $decryptData = ""; $errCode = $pc->decryptData($encryptedData, $iv, $decryptData); if ($errCode == 0) { return json_decode($decryptData, true); } return false; } /** * 发送HTTP GET请求 * @param $url * @return bool|string */ private function httpGet($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 500); curl_setopt($curl, CURLOPT_URL, $url); $res = curl_exec($curl); curl_close($curl); return $res; } } ``` 注意:这里使用了一个解密类`WXBizDataCrypt`,因为小程序的加密算法与微信小程序相同。这个类可以从微信官方示例代中获得(https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html)。 将微信官方提供的解密类`wxBizDataCrypt.php`放到项目中,然后在控制器中引入。微信官方提供的解密类如下(由于篇幅,这里只给出类名,具体代请从官方下载): ```php class WXBizDataCrypt { private $appid; private $sessionKey; public function __construct($appid, $sessionKey) { $this->sessionKey = $sessionKey; $this->appid = $appid; } public function decryptData($encryptedData, $iv, &$data) { if (strlen($this->sessionKey) != 24) { return -41001; } if (strlen($iv) != 24) { return -41002; } $aesKey = base64_decode($this->sessionKey); $aesIV = base64_decode($iv); $aesCipher = base64_decode($encryptedData); $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV); if ($result === false) { return -41003; } $dataObj = json_decode($result); if ($dataObj === null) { return -41003; } if ($dataObj->watermark->appid != $this->appid) { return -41003; } $data = $result; return 0; } } ``` ### 三、注意事项 1. 确保小程序后台已经配置了服务器域名(request合法域名),包括后端接口的域名。 2.开放平台获取正确的appid和appsecret。 3. 解密过程中,需要保证session_key的有效性。同一个session_key不能同时解密多次,如果解密失败,可能需要重新获取session_key。 4. 用户手机号属于敏感信息,注意存储和使用的合规性。 ### 四、测试 按照上述步骤完成后,可以进行测试。点击按钮获取手机号,查看后端解密是否成功。 ### 相关问题 1. 小程序获取手机号接口的权限如何申请? 2. 如何处理用户拒绝授权手机号的情况? 3. 小程序中,除了手机号,还能获取哪些用户信息? 4. 如何保证用户手机号在传输过程中的安全性? 5. ThinkPHP5.0中如何有效管理敏感配置信息(如appsecret)?</think>在UniApp开发小程序中实现一键获取用户手机号功能,需结合前端(UniApp)和后端(ThinkPHP 5.0)协同完成。以下是完整实现方案: ### 一、前端实现(UniApp部分) 1. **配置manifest.json** 确保小程序权限声明: ```json "mp-toutiao": { "appid": "你的小程序AppID", "permission": { "scope.userPhoneNumber": { "desc": "获取您的手机号" } } } ``` 2. **页面按钮触发** ```vue <button open-type="getPhoneNumber" @getphonenumber="onGetPhone">获取手机号</button> ``` 3. **事件处理逻辑** ```javascript export default { methods: { async onGetPhone(e) { if (e.detail.errMsg === 'getPhoneNumber:ok') { // 获取加密数据 const { encryptedData, iv } = e.detail; // 获取登录凭证 const [loginErr, loginRes] = await uni.login({ provider: 'toutiao' }); if (loginErr) return; // 发送到后端解密 const [res] = await uni.request({ url: 'https://your-domain.com/api/decrypt_phone', method: 'POST', data: { code: loginRes.code, encryptedData, iv } }); if (res.data.code === 200) { console.log('手机号:', res.data.phone); } } } } } ``` ### 二、后端实现ThinkPHP 5.0) 1. **安装解密扩展** ```bash composer require liliuwei/thinkphp-socialite ``` 2. **创建解密接口** ```php // application/api/controller/User.php namespace app\api\controller; use think\Controller; use think\Request; use Overtrue\Socialite\Providers\Toutiao; class User extends Controller { public function decryptPhone() { $data = Request::instance()->post(); // 配置参数 $config = [ 'app_id' => 'tt_your_appid', 'app_secret' => 'your_app_secret', ]; // 获取session_key $provider = new Toutiao($config); $session = $provider->jscode2session($data['code']); // 解密手机号 $decrypted = $provider->decryptData( $session['session_key'], $data['iv'], $data['encryptedData'] ); // 返回结果 return json([ 'code' => 200, 'phone' => $decrypted['phoneNumber'] ]); } } ``` 3. **路由配置** ```php // application/route.php Route::post('api/decrypt_phone', 'api/user/decryptPhone'); ``` ### 三、关键安全配置 1. **开放平台配置** - 在[开放平台](https://developer.open-douyin.com/)配置服务器域名 - 开启**手机号**权限 2. **HTTPS强制要求** 小程序所有请求必须使用HTTPS,在Nginx配置: ```nginx server { listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; } ``` ### 四、测试流程 1. 前端触发按钮获取加密数据 2. 后端接收参数并解密 3. 返回解密后的手机号 4. 控制台输出形如:`手机号: 13800138000` > ⚠️ **注意事项** > 1. 用户必须已授权手机号权限 > 2. `session_key`有效期5分钟,需及时解密 > 3. 手机号解密结果包含国家代(如`+86`) > 4. 生产环境需添加请求签名验证防止伪造请求 ### 五、常见问题解决 - **解密失败(41003)**:检查`session_key`是否与加密数据匹配 - **无效code(40029)**:确保code未重复使用 - **权限错误(10006)**:确认开放平台已开启手机号权限 --- ###
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

于先生吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值