You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
9.6 KiB
287 lines
9.6 KiB
9 months ago
|
<?php
|
||
|
|
||
|
namespace service;
|
||
|
|
||
|
use EasyWeChat\Foundation\Application;
|
||
|
use EasyWeChat\Payment\Order;
|
||
|
use think\exception\ValidateException;
|
||
|
use think\Url;
|
||
|
use app\admin\model\wechat\WechatMessage;
|
||
|
use service\SystemConfigService;
|
||
|
use service\AdminException;
|
||
|
use service\HookService;
|
||
|
use Exception;
|
||
|
use EasyWeChat\Encryption\EncryptionException;
|
||
|
|
||
|
/**快手小程序接口
|
||
|
* Class WechatMinService
|
||
|
* @package service
|
||
|
*/
|
||
|
class KuaiMiniProgramService
|
||
|
{
|
||
|
|
||
|
protected static $instance;
|
||
|
const KS_LINK = 'https://open.kuaishou.com/oauth2/mp/';
|
||
|
const KS_PAY = 'https://open.kuaishou.com/openapi/mp/developer/epay/';
|
||
|
const CODE2_SESSION_URL = self::KS_LINK . 'code2session';
|
||
|
const ACCESS_TOKEN_URL = 'https://open.kuaishou.com/oauth2/access_token';
|
||
|
const CREATE_ORDER = self::KS_PAY . 'create_order';
|
||
|
const REFUND_ORDER_URL='https://open.kuaishou.com/openapi/mp/developer/epay/apply_refund';
|
||
|
protected $appid;
|
||
|
protected $appSecret;
|
||
|
public $data = null;
|
||
|
public function __construct()
|
||
|
{
|
||
|
$kuaishou = SystemConfigService::more(['site_url', 'kuai_mini_appid', 'kuai_mini_secret']);
|
||
|
$this->appid = isset($kuaishou['kuai_mini_appid']) ? trim($kuaishou['kuai_mini_appid']) : '';
|
||
|
$this->appSecret = isset($kuaishou['kuai_mini_secret']) ? trim($kuaishou['kuai_mini_secret']) : '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获得用户信息 根据code 获取session_key
|
||
|
* @param array|string $openid
|
||
|
* @return $userInfo
|
||
|
*/
|
||
|
public function getUserInfo($code)
|
||
|
{
|
||
|
try {
|
||
|
$dat = array(
|
||
|
'app_id' => $this->appid,
|
||
|
'app_secret' => $this->appSecret,
|
||
|
'js_code' => $code
|
||
|
);
|
||
|
if (isset($code)) {
|
||
|
$res = self::curlPost(self::CODE2_SESSION_URL, $dat);
|
||
|
$data = json_decode($res, true);
|
||
|
if ($data['result'] == 1) {
|
||
|
return $data;
|
||
|
} else {
|
||
|
throw new ValidateException("获取失败");
|
||
|
}
|
||
|
} else {
|
||
|
throw new ValidateException("获取失败");
|
||
|
}
|
||
|
} catch (\Throwable $e) {
|
||
|
throw new ValidateException($e->getMessage());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 加密数据解密
|
||
|
* @param $sessionKey
|
||
|
* @param $iv
|
||
|
* @param $encryptData
|
||
|
* @return $userInfo
|
||
|
*/
|
||
|
public static function encryptor($sessionKey, $iv, $encrypted)
|
||
|
{
|
||
|
try {
|
||
|
$decrypted = openssl_decrypt(
|
||
|
base64_decode($encrypted, true),
|
||
|
'aes-128-cbc',
|
||
|
base64_decode($sessionKey, true),
|
||
|
OPENSSL_RAW_DATA | OPENSSL_NO_PADDING,
|
||
|
base64_decode($iv, true)
|
||
|
);
|
||
|
} catch (Exception $e) {
|
||
|
throw new EncryptionException($e->getMessage(), EncryptionException::ERROR_DECRYPT_AES);
|
||
|
}
|
||
|
|
||
|
if (is_null($result = json_decode(self::decode($decrypted), true))) {
|
||
|
throw new EncryptionException('ILLEGAL_BUFFER', EncryptionException::ILLEGAL_BUFFER);
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
public static function decode($decrypted)
|
||
|
{
|
||
|
$pad = ord(substr($decrypted, -1));
|
||
|
if ($pad < 1 || $pad > 32) {
|
||
|
$pad = 0;
|
||
|
}
|
||
|
return substr($decrypted, 0, (strlen($decrypted) - $pad));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 生成支付订单对象
|
||
|
* @param $openid
|
||
|
* @param $out_trade_no
|
||
|
* @param $total_fee
|
||
|
* @param $attach
|
||
|
* @param $body
|
||
|
* @param string $detail
|
||
|
* @param string $trade_type
|
||
|
* @param array $options
|
||
|
* @return Order
|
||
|
*/
|
||
|
protected static function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
|
||
|
{
|
||
|
$total_fee = bcmul($total_fee, 100, 0);
|
||
|
$order = array_merge(compact('openid', 'out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
|
||
|
if ($order['detail'] == '') unset($order['detail']);
|
||
|
return new Order($order);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 获得jsSdk支付参数
|
||
|
* @param $openid
|
||
|
* @param $out_trade_no
|
||
|
* @param $total_fee
|
||
|
* @param $attach
|
||
|
* @param $body
|
||
|
* @param string $detail
|
||
|
* @param string $trade_type
|
||
|
* @param array $options
|
||
|
* @return array|string
|
||
|
*/
|
||
|
public function jsPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
|
||
|
{
|
||
|
|
||
|
$time = time();
|
||
|
$price = $total_fee*100;
|
||
|
$config = [
|
||
|
'access_token' =>self::get_token(),
|
||
|
'app_id' => $this->appid,
|
||
|
];
|
||
|
$data = [
|
||
|
'open_id' => $openid,
|
||
|
'out_order_no' => $out_trade_no, //订单号
|
||
|
'total_amount' => $price, //金额 单位:分
|
||
|
'detail' => $body, //支付的内容
|
||
|
'subject' =>$body, //支付的标题
|
||
|
'type' => 1953,
|
||
|
'attach'=> $attach,
|
||
|
'expire_time' => 3600,
|
||
|
'notify_url'=>SystemConfigService::get('site_url') . Url::build('wap/Kuaishou/notify'),
|
||
|
];
|
||
|
$data['sign'] = self::generate_sign($config,$data);
|
||
|
$url = self::CREATE_ORDER.'?' . http_build_query($config);
|
||
|
$json = json_encode($data, 320);
|
||
|
$res = self::jsonPost($url, $json);
|
||
|
$res=json_decode($res,true);
|
||
|
if($res['result']==1)
|
||
|
return $res['order_info'];
|
||
|
else throw new ValidateException($res['error_msg']);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
public function generate_sign($query, $postData)
|
||
|
{
|
||
|
unset($query['access_token']);
|
||
|
$arr = array_merge($query, $postData);
|
||
|
foreach ($arr as $k => $item) {
|
||
|
if (empty($item)) {
|
||
|
unset($arr[$k]);
|
||
|
}
|
||
|
}
|
||
|
ksort($arr, 2);
|
||
|
$str = '';
|
||
|
foreach ($arr as $k => $v) {
|
||
|
$str .= $k . '=' . $v . '&';
|
||
|
}
|
||
|
$str = substr($str, 0, strlen($str) - 1);
|
||
|
$md5 = $str . $this->appSecret;
|
||
|
return md5($md5);
|
||
|
}
|
||
|
public function get_token()
|
||
|
{
|
||
|
try {
|
||
|
$postData['app_id'] = $this->appid;
|
||
|
$postData['app_secret'] = $this->appSecret;
|
||
|
$postData['grant_type'] = 'client_credentials';
|
||
|
$res = self::curlPost(self::ACCESS_TOKEN_URL, $postData);
|
||
|
$res = json_decode($res, 1);
|
||
|
return $res['access_token'];
|
||
|
} catch (\Throwable $e) {
|
||
|
throw new ValidateException($e->getMessage());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** 根据订单号退款
|
||
|
* @param $orderNo
|
||
|
* @param array $opt
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function createRefund($order){
|
||
|
$config = [
|
||
|
'access_token' => $this->get_token(),
|
||
|
'app_id' => $this->appid,
|
||
|
];
|
||
|
$params = [
|
||
|
'out_order_no' => (string)$order['order_id'],
|
||
|
'out_refund_no' => 'ks_refund_'. (string)$order['order_id'],
|
||
|
'reason' =>$order['refund_reason_wap_explain'] ?? '用户申请退款',
|
||
|
'notify_url' => SystemConfigService::get('site_url') . Url::build('wap/kuaishou/refundNotify'),
|
||
|
'refund_amount' =>(int)($order['refund_price']*100),
|
||
|
];
|
||
|
$params['sign'] = $this->generate_sign($config,$params);
|
||
|
$url = self::REFUND_ORDER_URL.'?'. http_build_query($config);
|
||
|
$json = json_encode($params, 320);
|
||
|
$res = $this->jsonPost($url, $json);
|
||
|
return json_decode($res, true);
|
||
|
}
|
||
|
|
||
|
|
||
|
public function jsonPost($url, $data = NULL, $times = 0)
|
||
|
{
|
||
|
$curl = curl_init();
|
||
|
curl_setopt($curl, CURLOPT_URL, $url);
|
||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||
|
curl_setopt($curl, CURLOPT_POST, 1);
|
||
|
curl_setopt($curl, CURLOPT_TIMEOUT, 2); //超时时间2秒
|
||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
||
|
curl_setopt($curl, CURLOPT_HEADER, 0);
|
||
|
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
|
||
|
'Content-Type: application/json; charset=utf-8',
|
||
|
'Content-Length:' . strlen($data),
|
||
|
'Cache-Control: no-cache',
|
||
|
'Pragma: no-cache'
|
||
|
));
|
||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||
|
$res = curl_exec($curl);
|
||
|
curl_close($curl);
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
public static function curlGet($url = '', $options = array())
|
||
|
{
|
||
|
$ch = curl_init($url);
|
||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||
|
if (!empty($options)) {
|
||
|
curl_setopt_array($ch, $options);
|
||
|
}
|
||
|
//https请求 不验证证书和host
|
||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
|
$data = curl_exec($ch);
|
||
|
curl_close($ch);
|
||
|
return $data;
|
||
|
}
|
||
|
public function curlPost($url = '', $postData = '', $options = array())
|
||
|
{
|
||
|
if (is_array($postData)) {
|
||
|
$postData = http_build_query($postData);
|
||
|
}
|
||
|
$ch = curl_init();
|
||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
|
||
|
if (!empty($options)) {
|
||
|
curl_setopt_array($ch, $options);
|
||
|
}
|
||
|
//https请求 不验证证书和host
|
||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
|
$data = curl_exec($ch);
|
||
|
curl_close($ch);
|
||
|
return $data;
|
||
|
}
|
||
|
}
|