WechatMinPay.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Name: 芸众商城系统
  5. * Author: 广州市芸众信息科技有限公司
  6. * Profile: 广州市芸众信息科技有限公司位于国际商贸中心的广州,专注于移动电子商务生态系统打造,拥有芸众社交电商系统、区块链数字资产管理系统、供应链管理系统、电子合同等产品/服务。官网 :www.yunzmall.com www.yunzshop.com
  7. * Date: 2022/6/27
  8. * Time: 11:08
  9. */
  10. namespace app\common\services\payment;
  11. use app\common\exceptions\AppException;
  12. use app\common\facades\EasyWeChat;
  13. use app\common\helpers\Url;
  14. use app\common\models\Member;
  15. use app\common\models\OrderPay;
  16. use app\common\models\PayOrder;
  17. use app\common\models\WechatMinAppPayOrder;
  18. use app\common\services\Pay;
  19. use app\common\services\Utils;
  20. use app\common\services\WechatPay;
  21. use GuzzleHttp\Client;
  22. use Illuminate\Support\Facades\Redis;
  23. use app\common\models\PayType;
  24. class WechatMinPay extends Pay
  25. {
  26. public $minSet;
  27. /**
  28. * WechatMinPay constructor.
  29. * @throws AppException
  30. */
  31. public function __construct()
  32. {
  33. $this->minSet = \Setting::get('plugin.min_app');
  34. if (!$this->minSet['secret'] || !$this->minSet['key'] || !$this->minSet['mchid']) {
  35. throw new AppException('小程序支付配置未设置');
  36. }
  37. }
  38. //发送获取token请求,获取token(有效期2小时)
  39. public function getToken()
  40. {
  41. // todo 不能做缓存如果其他地方调用了重新生成access_token,则这个旧的就没用了
  42. // $cacheKey = $this->minSet['key'].'_token_'.\YunShop::app()->uniacid;
  43. //
  44. // $cache_access_token = Redis::get($cacheKey);
  45. // if ($cache_access_token) {
  46. // return $cache_access_token;
  47. // }
  48. $paramMap = [
  49. 'grant_type' => 'client_credential',
  50. 'appid' => $this->minSet['key'],
  51. 'secret' => $this->minSet['secret'],
  52. ];
  53. //获取token的url参数拼接
  54. $strQuery="";
  55. foreach ($paramMap as $k=>$v){
  56. $strQuery .= strlen($strQuery) == 0 ? "" : "&";
  57. $strQuery.=$k."=".urlencode($v);
  58. }
  59. $getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?". $strQuery; //获取token的url
  60. $client = new Client;
  61. $res = $client->request('GET', $getTokenUrl);
  62. $data = json_decode($res->getBody()->getContents(), JSON_FORCE_OBJECT);
  63. if ($data['errcode']) {
  64. \Log::debug('===小程序支付管理获取token失败====='. self::class, $data);
  65. return false;
  66. }
  67. // if ($data['access_token']) {
  68. // Redis::setex($cacheKey, 600, $data['access_token']);
  69. // }
  70. return $data['access_token'];
  71. }
  72. /**
  73. * 请求接口-查询订单详情
  74. * @param $trade_no
  75. * @return mixed
  76. * @throws AppException
  77. * @throws \GuzzleHttp\Exception\GuzzleException
  78. */
  79. public function selectOrder($trade_no)
  80. {
  81. $access_token = $this->getToken();
  82. if (!$access_token) {
  83. throw new AppException('小程序获取access_token失败');
  84. }
  85. $url = 'https://api.weixin.qq.com/shop/pay/getorder?access_token='.$access_token;
  86. $client = new Client();
  87. $res = $client->request('POST', $url, ['json'=>['trade_no' => $trade_no]]);
  88. $result = json_decode($res->getBody()->getContents(), JSON_FORCE_OBJECT);
  89. if ($result['errcode']) {
  90. \Log::debug('小程序支付管理支付查询订单失败', $result);
  91. throw new AppException($result['errmsg']);
  92. }
  93. return $result['order'];
  94. }
  95. public function divideAccount($payOrder, $cacheToken = false)
  96. {
  97. //缓存 token
  98. if ($cacheToken) {
  99. $cacheKey = $this->minSet['key'].'_token_'.\YunShop::app()->uniacid;
  100. $cache_access_token = Redis::get($cacheKey);
  101. if (!$cache_access_token) {
  102. $access_token = $this->getToken();
  103. Redis::setex($cacheKey, 600, $access_token);
  104. } else {
  105. $access_token = $cache_access_token;
  106. }
  107. } else {
  108. $access_token = $this->getToken();
  109. }
  110. if (!$access_token) {
  111. return [ "errcode" => 99999, "errmsg" => "小程序获取access_token失败"];
  112. }
  113. //默认用支付单号做分账单号
  114. if (!$payOrder->divide_no) {
  115. $payOrder->divide_no = $payOrder->trade_no;
  116. }
  117. $parameter = [
  118. 'openid' => $payOrder->openid,
  119. 'mchid' => $this->minSet['mchid'],
  120. 'trade_no' => $payOrder->trade_no,
  121. 'transaction_id' => $payOrder->transaction_id,
  122. 'profit_sharing_no' => $payOrder->divide_no,
  123. ];
  124. $url = 'https://api.weixin.qq.com/shop/pay/profitsharingorder?access_token='.$access_token;
  125. $result = $this->curlRequest($url, json_encode($parameter, JSON_UNESCAPED_UNICODE));
  126. if (!is_array($result)) {
  127. $result = json_decode($result, true);
  128. }
  129. return $result;
  130. }
  131. public function newPay($data)
  132. {
  133. $text = $data['extra']['type'] == 1 ? '支付' : '充值';
  134. $op = '微信小程序' . $text . ' 订单号:' . $data['order_no'];
  135. $pay_order_model = $this->log($data['extra']['type'], '微信小程序new', $data['amount'], $op, $data['order_no'], Pay::ORDER_STATUS_NON, \YunShop::app()->getMemberId());
  136. if (empty(\YunShop::app()->getMemberId())) {
  137. throw new AppException('无法获取用户ID');
  138. }
  139. $strName = preg_replace("/[^\x{4e00}-\x{9fa5}a-zA-Z0-9\.]/u",'',$data['subject']);
  140. $parameter = [
  141. 'openid' => $this->getMinOpenId(\YunShop::app()->getMemberId()),
  142. // 'combine_trade_no' => \app\common\services\CreateRandomNumber::randomNumber('CXC'),
  143. 'combine_trade_no' => 'CXC'.substr($data['order_no'], 2,17),
  144. 'expire_time' => time() + (1 * 60 * 60),
  145. 'sub_orders' => [
  146. [
  147. 'mchid' => $this->minSet['mchid'],
  148. 'amount' => $data['amount'] * 100, // 单位:分
  149. 'trade_no' => $data['order_no'],
  150. 'description' => $this->cutStr($strName,50),
  151. ],
  152. ],
  153. ];
  154. //请求数据日志
  155. self::payRequestDataLog($data['order_no'], $pay_order_model->type,
  156. $pay_order_model->third_type, json_encode($parameter));
  157. $access_token = $this->getToken();
  158. if (!$access_token) {
  159. throw new AppException('小程序获取access_token失败');
  160. }
  161. $url = 'https://api.weixin.qq.com/shop/pay/createorder?access_token='.$access_token;
  162. $result = $this->curlRequest($url, json_encode($parameter, JSON_UNESCAPED_UNICODE));
  163. if (!is_array($result)) {
  164. $result = json_decode($result, true);
  165. }
  166. // $client = new Client();
  167. // $res = $client->request('POST', $url, ['json'=>$parameter]);
  168. //
  169. // $result = json_decode($res->getBody()->getContents(), JSON_FORCE_OBJECT);
  170. if ($result['errcode']) {
  171. \Log::debug('小程序支付管理支付请求失败', $result);
  172. \Log::debug('小程序支付管理支付请求失败', $parameter);
  173. throw new AppException($result['errmsg']);
  174. }
  175. $this->savePayOrder($data['order_no'], $parameter['openid']);
  176. $config = $result['payment_params'];
  177. $config['appId'] = $this->minSet['key'];
  178. $config['timestamp'] = $config['timeStamp'];
  179. $config['is_pay_manager'] = 1;
  180. // \Log::debug('小程序支付管理支付', $config);
  181. return $config;
  182. }
  183. /**
  184. * 垃圾微信,需要在这里先保存用户的openid因为分账时需要openid
  185. * @param $trade_no
  186. * @param $openid
  187. */
  188. public function savePayOrder($trade_no,$openid)
  189. {
  190. $payRecord = WechatMinAppPayOrder::existOrNew($trade_no);
  191. $payRecord->fill(['trade_no' => $trade_no, 'openid' => $openid]);
  192. return $payRecord->save();
  193. }
  194. /**
  195. * @param $data
  196. * @return array|mixed
  197. * @throws AppException
  198. */
  199. public function doPay($data)
  200. {
  201. //小程序没有被灰度的走微信统一下单接口,
  202. if (!$this->minSet['is_pay_manager']) {
  203. $config = $this->oldPay($data);
  204. } else {
  205. $config = $this->newPay($data);
  206. }
  207. //视频号场景下的直接走原来的微信支付方法
  208. if (app('plugins')->isEnabled('wechat-trade') && isset(request()->wechat_trade_order_type)) {
  209. \Log::debug('------小程序视频号支付-------');
  210. $config = $this->wechatTradePay($config);
  211. }
  212. return ['config'=>$config];
  213. }
  214. //小程序自定义版交易组件及开放接口
  215. //文档 https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/ministore/minishopopencomponent2/API/order/add_order_new.html
  216. //暂时写到这里,因为不知道需要什么参数
  217. //优化需要把这个写到插件里
  218. public function wechatTradePay($config)
  219. {
  220. $order_pay = OrderPay::find(request()->order_pay_id);
  221. $trade_data = [
  222. 'pay_type' => 0,
  223. 'prepay_time' => $config['timestamp'],
  224. 'prepay_id' => $config['prepayId'],
  225. 'pay_sn' => $order_pay->pay_sn,
  226. 'scene' => request()->senceKey,
  227. 'order_type' => request()->wechat_trade_order_type,
  228. ];
  229. $wechat_trade_order_info = (new \Yunshop\WechatTrade\services\AddOrderService($trade_data))->handle();
  230. if (!$wechat_trade_order_info['result']) {
  231. throw new AppException($wechat_trade_order_info['msg']);
  232. }
  233. if (request()->wechat_trade_order_type == 1) {
  234. foreach ($wechat_trade_order_info['data']['payment_params'] as $k => $v) {
  235. $config[$k] = $v;
  236. }
  237. } else {
  238. $config['order_info'] = $wechat_trade_order_info['data']['data'];
  239. }
  240. return $config;
  241. }
  242. public function oldPay($data)
  243. {
  244. if (empty(\YunShop::app()->getMemberId())) {
  245. throw new AppException('无法获取用户ID');
  246. }
  247. $text = $data['extra']['type'] == 1 ? '支付' : '充值';
  248. $op = '微信小程序' . $text . ' 订单号:' . $data['order_no'];
  249. $pay_order_model = $this->log($data['extra']['type'], '微信小程序old', $data['amount'], $op, $data['order_no'], Pay::ORDER_STATUS_NON, \YunShop::app()->getMemberId());
  250. $openId = $this->getMinOpenId(\YunShop::app()->getMemberId());
  251. $attributes = [
  252. 'trade_type' => 'JSAPI', //小程序微信支付
  253. 'body' => mb_substr($data['subject'], 0, 40),
  254. 'out_trade_no' => $data['order_no'],
  255. 'total_fee' => $data['amount'] * 100, // 单位:分
  256. 'nonce_str' => \app\common\helpers\Client::random(8) . "",
  257. 'device_info' => 'yun_shop',
  258. 'attach' => \YunShop::app()->uniacid . ':wechat:2',
  259. 'spbill_create_ip' => self::getClientIP(),
  260. 'openid' => $openId,
  261. ];
  262. //请求数据日志
  263. self::payRequestDataLog($attributes['out_trade_no'], $pay_order_model->type,
  264. $pay_order_model->third_type, json_encode($attributes));
  265. $notify_url = Url::shopSchemeUrl('payment/wechat/notifyUrl.php');
  266. $payment = $this->getEasyWeChatApp($notify_url);
  267. $result = $payment->order->unify($attributes);
  268. \Log::debug('--微信支付小程序预下单--', $result);
  269. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  270. $prepayId = $result['prepay_id'];
  271. $pay_order_model->fill(['status'=> Pay::ORDER_STATUS_WAITPAY])->save();
  272. } elseif ($result['return_code'] == 'SUCCESS') {
  273. throw new AppException($result['err_code_des']);
  274. } else {
  275. throw new AppException($result['return_msg']);
  276. }
  277. $config = $payment->jssdk->sdkConfig($prepayId);
  278. $config['prepayId'] = $prepayId;
  279. return $config;
  280. }
  281. public function doRefund($out_trade_no, $totalmoney, $refundmoney)
  282. {
  283. if (!$this->minSet['is_pay_manager']) {
  284. return $this->oldRefund($out_trade_no, $totalmoney, $refundmoney);
  285. }
  286. //退款单号不能超过20位
  287. $unique = substr(uniqid(), strlen(\YunShop::app()->uniacid) + 3);
  288. $out_refund_no = date('Ymd', time()) . \YunShop::app()->uniacid . $unique;
  289. // $out_refund_no = $this->setUniacidNo(\YunShop::app()->uniacid);
  290. $op = '微信退款 订单号:' . $out_trade_no . '退款单号:' . $out_refund_no . '退款金额:' . $refundmoney;
  291. if (empty($out_trade_no)) {
  292. throw new AppException('参数错误');
  293. }
  294. $pay_order_model = $this->refundlog(Pay::PAY_TYPE_REFUND, '微信小程序支付管理退款', $refundmoney, $op, $out_trade_no, Pay::ORDER_STATUS_NON, 0);
  295. $member_id = OrderPay::select('uid')->where('pay_sn', $out_trade_no)->value('uid');
  296. $transaction_id = PayOrder::select('trade_no')
  297. ->where('out_order_no', $out_trade_no)
  298. ->where('trade_no', '!=', '0')
  299. ->value('trade_no');
  300. $parameter = [
  301. 'openid' => $this->getMinOpenId($member_id),
  302. 'mchid' => $this->minSet['mchid'],
  303. 'trade_no' => $out_trade_no,
  304. 'transaction_id' => $transaction_id,
  305. 'total_amount' => intval(bcmul($totalmoney, 100,0)), //这里必须是数字number类型,不能是字符串类型
  306. 'refund_amount' => intval(bcmul($refundmoney, 100,0)),
  307. 'refund_no' => $out_refund_no,
  308. ];
  309. $access_token = $this->getToken();
  310. if (!$access_token) {
  311. throw new AppException('小程序获取access_token失败');
  312. }
  313. $url = 'https://api.weixin.qq.com/shop/pay/refundorder?access_token='.$access_token;
  314. $client = new Client();
  315. $res = $client->request('POST', $url, ['json'=>$parameter]);
  316. $result = json_decode($res->getBody()->getContents(), JSON_FORCE_OBJECT);
  317. $this->payResponseDataLog($out_trade_no, '微信小程序支付管理退款', json_encode(array_merge($parameter,$result)));
  318. if ($result['errcode']) {
  319. \Log::debug('---小程序支付管理退款申请失败---', $result);
  320. throw new AppException($result['errmsg']);
  321. }
  322. $pay_order_model->fill([
  323. 'status'=> Pay::ORDER_STATUS_COMPLETE,
  324. 'trade_no' => $result['transaction_id']?:'',
  325. ])->save();
  326. return true;
  327. }
  328. /**
  329. * 微信退款
  330. *
  331. * @param 订单号 $out_trade_no
  332. * @param 订单总金额 $totalmoney
  333. * @param 退款金额 $refundmoney
  334. * @return array|mixed
  335. */
  336. public function oldRefund($out_trade_no, $totalmoney, $refundmoney)
  337. {
  338. $out_refund_no = $this->setUniacidNo(\YunShop::app()->uniacid);
  339. $op = '微信小程序退款 订单号:' . $out_trade_no . '退款单号:' . $out_refund_no . '退款总金额:' . $totalmoney;
  340. if (empty($out_trade_no)) {
  341. throw new AppException('参数错误');
  342. }
  343. $pay_type_id = OrderPay::get_paysn_by_pay_type_id($out_trade_no);
  344. $pay_type_name = PayType::get_pay_type_name($pay_type_id);
  345. $pay_order_model = $this->refundlog(Pay::PAY_TYPE_REFUND, $pay_type_name, $refundmoney, $op, $out_trade_no, Pay::ORDER_STATUS_NON, 0);
  346. if (empty($this->minSet['apiclient_cert']) || empty($this->minSet['apiclient_key'])) {
  347. throw new AppException('未上传完整的微信支付证书,请到【系统设置】->【支付方式】中上传!');
  348. }
  349. $notify_url = '';
  350. $payment = $this->getEasyWeChatApp($notify_url);
  351. try {
  352. $totalmoney = bcmul($totalmoney, 100,0);
  353. $refundmoney = bcmul($refundmoney, 100,0);
  354. $result = $payment->refund->byOutTradeNumber($out_trade_no, $out_refund_no, $totalmoney, $refundmoney);
  355. } catch (\Exception $e) {
  356. \Log::debug('---微信退款接口请求错误---', $e->getMessage());
  357. throw new AppException('微信接口错误:' . $e->getMessage());
  358. }
  359. $this->payResponseDataLog($out_trade_no, '微信小程序退款', json_encode($result));
  360. //微信申请退款失败
  361. if (isset($result['result_code']) && strtoupper($result['result_code']) == 'FAIL') {
  362. \Log::debug('---微信退款申请错误---', $result);
  363. throw new AppException('微信退款申请错误:'.$result['err_code'] . '-' . $result['err_code_des']);
  364. }
  365. $status = $this->queryRefund($payment, $out_trade_no);
  366. \Log::debug('---退款状态---', [$status]);
  367. if ($status == 'PROCESSING' || $status == 'SUCCESS' || ($status == 'fail' && $result->refund_id)){
  368. $pay_order_model->fill([
  369. 'status'=> Pay::ORDER_STATUS_COMPLETE,
  370. 'trade_no' => $result['transaction_id']?:'',
  371. ])->save();
  372. return true;
  373. } else {
  374. \Log::debug('---微信退款接口返回错误---', $result);
  375. throw new AppException('微信接口错误:'.$result['return_msg'] . '-' . $result['err_code_des'] . '/' . $status);
  376. }
  377. }
  378. /**
  379. * 订单退款查询
  380. *
  381. * @param $payment
  382. * @param $out_trade_no
  383. * @return mixed
  384. */
  385. public function queryRefund($payment, $out_trade_no)
  386. {
  387. $result = $payment->refund->queryByOutTradeNumber($out_trade_no);
  388. foreach ($result as $key => $value) {
  389. if (preg_match('/refund_status_\d+/', $key)) {
  390. return $value;
  391. }
  392. }
  393. return 'fail';
  394. }
  395. /**
  396. * 创建支付对象
  397. * @param $notify_url
  398. * @return \EasyWeChat\Payment\Payment
  399. */
  400. public function getEasyWeChatApp($notify_url)
  401. {
  402. $options = [
  403. 'app_id' => $this->minSet['key'],
  404. 'secret' => $this->minSet['secret'],
  405. 'mch_id' => $this->minSet['mchid'],
  406. 'key' => $this->minSet['api_secret'],
  407. 'cert_path' => $this->minSet['apiclient_cert'],
  408. 'key_path' => $this->minSet['apiclient_key'],
  409. 'notify_url' => $notify_url,
  410. ];
  411. $app = EasyWeChat::payment($options);
  412. return $app;
  413. }
  414. public function doWithdraw($member_id, $out_trade_no, $money, $desc, $type)
  415. {
  416. // TODO: Implement doWithdraw() method.
  417. }
  418. public function buildRequestSign()
  419. {
  420. // TODO: Implement buildRequestSign() method.
  421. }
  422. /**
  423. * @param $member_id
  424. * @return mixed|string
  425. * @throws AppException
  426. */
  427. public function getMinOpenId($member_id)
  428. {
  429. $openid = Member::getOpenIdForType($member_id, 2);
  430. if (empty($openid)) {
  431. throw new AppException('小程序用户openid不存在');
  432. }
  433. return $openid;
  434. }
  435. /**
  436. * @param $url
  437. * @param $post
  438. * @param int $timeout
  439. * @param bool $json
  440. * @return mixed|string
  441. */
  442. public function curlRequest($url, $post, $timeout = 60, $json = false)
  443. {
  444. $curl = curl_init();
  445. curl_setopt($curl, CURLOPT_URL, $url);
  446. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
  447. curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
  448. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  449. $headers[] = "Content-type:application/json;charset=UTF-8";//设置请求体类型
  450. // $headers[] = "Content-Type:application/x-www-form-urlencoded; charset=UTF-8";
  451. $headers[] = 'Content-Length: '.strlen($post);
  452. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  453. //设置打印请求头信息
  454. //curl_setopt($curl, CURLINFO_HEADER_OUT, true);
  455. if ($post) {
  456. curl_setopt($curl, CURLOPT_POST, 1);
  457. curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($post)?http_build_query($post):$post);
  458. }
  459. $TLS = substr($url, 0, 8) == "https://" ? true : false;
  460. if ($TLS) {
  461. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
  462. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
  463. }
  464. curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
  465. $data = curl_exec($curl);
  466. //设置打印请求头信息
  467. //$request_headers = curl_getinfo($curl, CURLINFO_HEADER_OUT);
  468. if (curl_errno($curl)) {
  469. return curl_error($curl);
  470. }
  471. curl_close($curl);
  472. if ($json) {
  473. return json_decode($data, true);
  474. }
  475. return $data;
  476. }
  477. /**
  478. * @param $sourcestr string 字符串
  479. * @param $cutlength integer 保留长度
  480. * @return string
  481. */
  482. public function cutStr($sourcestr, $cutlength) {
  483. $returnstr = '';
  484. $i = 0;
  485. $n = 0;
  486. $str_length = strlen ($sourcestr); //字符串的字节数
  487. while ( ($i < $cutlength) and ($i <= $str_length) ) {
  488. $temp_str = substr ( $sourcestr, $i, 1);
  489. $ascnum = Ord ($temp_str); //得到字符串中第$i位字符的ascii码
  490. if ($ascnum >= 224) {
  491. //如果ASCII位高与224
  492. $returnstr = $returnstr . substr($sourcestr, $i, 3); //根据UTF-8编码规范,将3个连续的字符计为单个字符
  493. $i = $i + 3; //实际Byte计为3
  494. $n ++; //字串长度计1
  495. } elseif ($ascnum >= 192) {
  496. //如果ASCII位高与192,
  497. $returnstr = $returnstr . substr ( $sourcestr, $i, 2); //根据UTF-8编码规范,将2个连续的字符计为单个字符
  498. $i = $i + 2; //实际Byte计为2
  499. $n ++; //字串长度计1
  500. } elseif ($ascnum >= 65 && $ascnum <= 90) {
  501. //如果是大写字母,
  502. $returnstr = $returnstr . substr ( $sourcestr, $i, 1);
  503. $i = $i + 1; //实际的Byte数仍计1个
  504. $n ++; //但考虑整体美观,大写字母计成一个高位字符
  505. } else {
  506. //其他情况下,包括小写字母和半角标点符号,
  507. $returnstr = $returnstr . substr ( $sourcestr, $i, 1);
  508. $i = $i + 1; //实际的Byte数计1个
  509. $n = $n + 0.5; //小写字母和半角标点等与半个高位字符宽...
  510. }
  511. }
  512. //小程序支付商品详情不能有 ...
  513. // if ($str_length > $cutlength) {
  514. // $returnstr = $returnstr . "..."; //超过长度时在尾处加上省略号
  515. // }
  516. return $returnstr;
  517. }
  518. }