初始化项目:添加后端代码、ThinkPHP框架、前端资源

This commit is contained in:
amb
2026-09-03 12:42:39 +08:00
commit 482bece22e
3726 changed files with 416708 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace douyin;
use app\common\exception\Exception;
/**
* Class AES.
*
* @author overtrue <i@overtrue.me>
*/
class AES
{
/**
* @param string $text
* @param string $key
* @param string $iv
* @param int $option
*
* @return string
*/
public static function encrypt(string $text, string $key, string $iv, int $option = OPENSSL_RAW_DATA): string
{
self::validateKey($key);
self::validateIv($iv);
return openssl_encrypt($text, self::getMode($key), $key, $option, $iv);
}
/**
* @param string $cipherText
* @param string $key
* @param string $iv
* @param int $option
* @param string|null $method
*
* @return string
*/
public static function decrypt(string $cipherText, string $key, string $iv, int $option = OPENSSL_RAW_DATA, $method = null): string
{
self::validateKey($key);
self::validateIv($iv);
return openssl_decrypt($cipherText, $method ?: self::getMode($key), $key, $option, $iv);
}
/**
* @param string $key
*
* @return string
*/
public static function getMode($key)
{
return 'aes-'.(8 * strlen($key)).'-cbc';
}
/**
* @param string $key
*/
public static function validateKey(string $key)
{
if (!in_array(strlen($key), [16, 24, 32], true)) {
throw new Exception(sprintf('Key length must be 16, 24, or 32 bytes; got key len (%s).', strlen($key)));
}
}
/**
* @param string $iv
*
* @throws \Exception
*/
public static function validateIv(string $iv)
{
if (!empty($iv) && 16 !== strlen($iv)) {
throw new Exception('IV length must be 16 bytes.');
}
}
}
+548
View File
@@ -0,0 +1,548 @@
<?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;
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace douyin;
use app\common\exception\Exception;
use douyin\AES;
/**
* Class Encryptor.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class Encryptor
{
/**
* Decrypt data.
*
* @param string $sessionKey
* @param string $iv
* @param string $encrypted
*
* @return array
*/
public function decryptData(string $sessionKey, string $iv, string $encrypted): array
{
$decrypted = AES::decrypt(
base64_decode($encrypted, false), base64_decode($sessionKey, false), base64_decode($iv, false)
);
$decrypted = json_decode($this->pkcs7Unpad($decrypted), true);
if (!$decrypted) {
throw new Exception('The given payload is invalid.');
}
return $decrypted;
}
const ERROR_INVALID_SIGNATURE = -40001; // Signature verification failed
const ERROR_CALC_SIGNATURE = -40003; // Calculating the signature failed
const ERROR_INVALID_AES_KEY = -40004; // Invalid AESKey
const ERROR_INVALID_APP_ID = -40005; // Check AppID failed
const ERROR_ENCRYPT_AES = -40006; // AES EncryptionInterface failed
const ERROR_DECRYPT_AES = -40007; // AES decryption failed
const ERROR_BASE64_ENCODE = -40009; // Base64 encoding failed
const ERROR_BASE64_DECODE = -40010; // Base64 decoding failed
const ILLEGAL_BUFFER = -41003; // Illegal buffer
/**
* @var string
*/
protected $aesKey;
/**
* Block size.
*
* @var int
*/
protected $blockSize = 32;
/**
* Constructor.
*
* @param string $appId
* @param string|null $token
* @param string|null $aesKey
*/
public function __construct(string $aesKey = null)
{
$this->aesKey = base64_decode($aesKey.'=', true);
}
/**
* Get SHA1.
*
* @return string
*
* @throws self
*/
public function signature(): string
{
$array = func_get_args();
sort($array, SORT_STRING);
return sha1(implode($array));
}
/**
* PKCS#7 pad.
*
* @param string $text
* @param int $blockSize
*
* @return string
*
*/
public function pkcs7Pad($text, $blockSize)
{
if ($blockSize > 256) {
throw new Exception('$blockSize may not be more than 256');
}
$padding = $blockSize - (strlen($text) % $blockSize);
$pattern = chr($padding);
return $text.str_repeat($pattern, $padding);
}
/**
* PKCS#7 unpad.
*
* @param string $text
*
* @return string
*/
public function pkcs7Unpad($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > $this->blockSize) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace douyin;
class order{
public function getByteAuthorization($data, $appId,$privateKeyStr, $keyVersion = "1", $nonceStr = '', $timestamp = '') {
if(!$timestamp){
$timestamp = time();
}
if(!$nonceStr){
$nonceStr = $this->randStr(10);
}
// 读取私钥
$privateKey = openssl_pkey_get_private($privateKeyStr);
if (!$privateKey) {
throw new InvalidArgumentException("Invalid private key");
}
// 生成签名
$signature = $this->getSignature("POST", "/requestOrder", $timestamp, $nonceStr, json_encode($data), $privateKey);
if ($signature === false) {
return null;
}
// 构造 byteAuthorization
$byteAuthorization = sprintf("SHA256-RSA2048 appid=%s,nonce_str=%s,timestamp=%s,key_version=%s,signature=%s", $appId, $nonceStr, $timestamp, $keyVersion, $signature);
return $byteAuthorization;
}
/**
* 获取签名
* @param $method
* @param $url
* @param $timestamp
* @param $nonce
* @param $data
* @param $privateKey
* @return string
*/
public function getSignature($method, $url, $timestamp, $nonce, $data, $privateKey) {
$targetStr = $method. "\n" . $url. "\n" . $timestamp. "\n" . $nonce. "\n" . $data. "\n";
openssl_sign($targetStr, $sign, $privateKey, OPENSSL_ALGO_SHA256);
$sign = base64_encode($sign);
return $sign;
}
public function randStr($length = 8) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
}
?>
+249
View File
@@ -0,0 +1,249 @@
<?php
namespace Douyin;
class Pay{
public $api_url='https://developer.toutiao.com/api/apps/ecpay/v1/';
public $app_id;
public $token;
public $salt;
public $notifyUrl;
public function __construct($config) {
if (empty($config['app_id'])) {
throw new \Exception('not empty app_id');
}
if (empty($config['secret'])) {
throw new \Exception('not empty secret');
}
if (empty($config['merchant_id'])) {
throw new \Exception('not empty merchant_id');
}
if (empty($config['salt'])) {
throw new \Exception('not empty salt');
}
if (empty($config['notify_url'])) {
throw new \Exception('not empty notify_url');
}
if (empty($config['token'])) {
throw new \Exception('not empty notify_url');
}
$this->app_id = $config['app_id'];
$this->token = $config['token'];
$this->salt = $config['salt'];
$this->notifyUrl = $config['notify_url'];
$this->store_uid = $config['merchant_id'];
}
public function run(){
$action=addslashes($_GET['ac']);
$action=$action?$action:'order';
if(!in_array($action,['order','query','refund','settle','notify','set'])){
echo '非法请求';die;
}
call_user_func(array($this,$action));
}
//下单
public function order($name,$price,$out_order_no){
$data=[
'out_order_no'=>$out_order_no,
'total_amount'=>$price,
'subject'=>$name,
'body'=>$name,
'store_uid'=>$this->store_uid,
'valid_time'=>7200
];
$res = $this->post('create_order',$data);
return $res;
}
//查询订单
public function query(){
$data=[
'out_order_no'=>'2021110117254573565'
];
$res=$this->post('query_order',$data,false);
echo json_encode($res);die;
}
//订单退款
public function refund(){
$data=[
'out_order_no'=>'2021110118351347832',
'out_refund_no'=>$this->order_number(),
'reason'=>'退款原因',
'refund_amount'=>1,
];
$res=$this->post('create_refund',$data);
echo json_encode($res);die;
}
//订单分账
public function settle(){
$data=[
'out_order_no'=>'2021110118301265990',
'out_settle_no'=>$this->order_number(),
'settle_desc'=>'分账描述',
'settle_params'=>json_encode([]),//分润方参数 如[['merchant_uid'=>'商户号','amount'=>'10']] 可以有多个分账商户
];
$res=$this->post('settle',$data);
echo json_encode($res);die;
}
//支付设置回调测试
public function set(){
$content=file_get_contents('php://input');
$this->log('log.txt',$content);
}
//回调
public function notify(){
$content=file_get_contents('php://input');
if(empty($content)) return false;
$this->log('notify.txt',$content);
$content=json_decode($content,true);
$sign=$this->handler($content);
if($sign==$content['msg_signature']){
$msg=json_decode($content['msg'],true);
echo '回调----'.$content['type']."\n";
//这里更新应用业务逻辑代码,使用$msg跟应用订单比对更新订单,可以用 $content['type']判断是支付回调还是退款回调,payment支付回调 refund退款回调。
$res=['err_no'=>0,'err_tips'=>'success'];
echo json_encode($res);
}
}
/**
* 测试订单号,实际应用根据自己应用实际生成
* @return string
*/
public function order_number(){
return date('YmdHis').rand(10000,99999);
}
/**
* 请求小程序平台服务端
* @param string $url 接口地址
* @param array $data 参数内容
* @param boolean $notify 是否有回调
* @return array
*/
public function post($method,$data,$notify=true){
$data['app_id'] = $this->app_id;
if(!empty($notify)){
$data['notify_url']=$this->notifyUrl;//也可以在调用的时候分别设置
}
$data['sign']=$this->sign($data);
$url=$this->api_url.$method;
$res=$this->http('POST',$url,json_encode($data),['Content-Type: application/json'],true);
return json_decode($res,true);
}
/**
* 回调验签
* @param array $map 验签参数
* @return stirng
*/
public function handler($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 array $map 请求参数
* @return stirng
*/
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 string $path 日志路径
* @param string $content 内容
*/
public function log($path, $content){
$file=fopen($path, "a");
fwrite($file, date('Y-m-d H:i:s').'-----'.$content."\n");
fclose($file);
}
/**
* 网络请求
* @param stirng $method 请求模式
* @param stirng $url请求网关
* @param array $params 请求参数
* @param stirng $header 自定义头
* @param boolean $multi 文件上传
* @return array
*/
public function http( $method = 'GET', $url,$params,$header = array(), $multi = false){
$opts = array(
CURLOPT_TIMEOUT => 30,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => $header
);
/* 根据请求类型设置特定参数 */
switch(strtoupper($method)){
case 'GET':
$opts[CURLOPT_URL] = $url . '?' . http_build_query($params);
break;
case 'POST':
//判断是否传输文件
$params = $multi ? $params : http_build_query($params);
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $params;
break;
default:
throw new Exception('不支持的请求方式!');
}
/* 初始化并执行curl请求 */
$ch = curl_init();
curl_setopt_array($ch, $opts);
$data = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if($error) throw new Exception('请求发生错误:' . $error);
return $data;
}
}
?>