Byte.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <?php
  2. namespace Ycpay;
  3. class Byte implements PayInterface
  4. {
  5. private $orderParam;
  6. private $app_id;
  7. private $secret;
  8. private $merchant_id;
  9. private $salt;
  10. private $valid_time;
  11. private $notify_url;
  12. private $settle_url;
  13. private $token;
  14. private $codeUrl = 'https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session?';
  15. private $tokenUrl = 'https://developer.toutiao.com/api/apps/v2/token';
  16. protected $payUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_order';
  17. protected $query = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_order';
  18. protected $refundUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_refund';
  19. protected $settle = 'https://developer.toutiao.com/api/apps/ecpay/v1/settle';
  20. protected $sendMsgUrl = 'https://developer.toutiao.com/api/apps/subscribe_notification/developer/v1/notify';
  21. private $notifyOrder;
  22. public static function init($config)
  23. {
  24. if (empty($config['app_id'])) {
  25. throw new \Exception('not empty app_id');
  26. }
  27. if (empty($config['secret'])) {
  28. throw new \Exception('not empty secret');
  29. }
  30. // if (empty($config['merchant_id'])) {
  31. // throw new \Exception('not empty merchant_id');
  32. // }
  33. // if (empty($config['salt'])) {
  34. // throw new \Exception('not empty salt');
  35. // }
  36. // if (empty($config['notify_url'])) {
  37. // throw new \Exception('not empty notify_url');
  38. // }
  39. // if (empty($config['token'])) {
  40. // throw new \Exception('not empty notify_url');
  41. // }
  42. $class = new self();
  43. $class->app_id = $config['app_id'];
  44. $class->secret = $config['secret'];
  45. $class->token = isset($config['token'])?$config['token']:'';
  46. $class->merchant_id = isset($config['merchant_id'])?$config['merchant_id']:'';
  47. $class->salt = isset($config['salt'])?$config['salt']:'';
  48. $class->settle_url = isset($config['settle_url']) ? $config['settle_url'] : '';
  49. $class->notify_url = isset($config['notify_url'])?$config['notify_url']:'';
  50. $class->valid_time = isset($config['valid_time']) ? $config['valid_time'] : time() + 900;
  51. return $class;
  52. }
  53. /**
  54. * 获取下单信息
  55. */
  56. public function getParam()
  57. {
  58. return $this->orderParam;
  59. }
  60. /**
  61. * 获取异步订单信息
  62. */
  63. public function getNotifyOrder()
  64. {
  65. $data = file_get_contents("php://input");
  66. $order = json_decode($data, true);
  67. $order['msg'] = json_decode($order['msg'], true);
  68. $this->notifyOrder = $order;
  69. return $this->notifyOrder;
  70. }
  71. /**
  72. * 获取token
  73. */
  74. public function getToken()
  75. {
  76. $arr = [
  77. 'grant_type' => 'client_credential',
  78. 'appid' => $this->app_id,
  79. 'secret' => $this->secret,
  80. ];
  81. return json_decode($this->curl_post($this->tokenUrl, json_encode($arr)), true);
  82. }
  83. /**
  84. * 获取 openid
  85. *
  86. * @param string $code
  87. * @param string $anonymous_code
  88. * @return void
  89. * @author LiJie
  90. */
  91. public function getOpenid($code, $anonymous_code = "")
  92. {
  93. $url = $this->codeUrl . "appid=" . $this->app_id . "&secret=" . $this->secret . "&code=" . $code;
  94. if ($anonymous_code) {
  95. $url .= "&anonymous_code=" . $anonymous_code;
  96. }
  97. return json_decode($this->curl_get($url), true);
  98. }
  99. /**
  100. * 异步回调
  101. * @param $order 回调数据
  102. * @return bool true 验签通过|false 验签不通过
  103. */
  104. public function notifyCheck()
  105. {
  106. $order = $this->getNotifyOrder();
  107. $data = [
  108. $order['timestamp'],
  109. $order['nonce'],
  110. json_encode($order['msg']),
  111. $this->token,
  112. ];
  113. sort($data, SORT_STRING);
  114. $str = implode('', $data);
  115. \Log::info('notifyCheck:mysign:'.sha1($str).' byte_sign:'.$order['msg_signature']);
  116. if (!strcmp(sha1($str), $order['msg_signature'])) {
  117. return true;
  118. }
  119. return false;
  120. }
  121. /**
  122. * 创建订单
  123. * 设置订单号 金额 描述
  124. * @param string $rder_no 平台订单号
  125. * @param int $money 订单金额
  126. * @param int $store_uid 商户号
  127. * @param string $title 描述
  128. *
  129. */
  130. public function applyOrderCreate($order_no, $money,$store_uid, $title, $desc = '',$phone_type)
  131. {
  132. $orderParam["out_order_no"] = $order_no;
  133. $orderParam["total_amount"] = $money;
  134. $orderParam["store_uid"] = $store_uid;
  135. $orderParam["subject"] = $title;
  136. $orderParam["body"] = $desc;
  137. $orderParam["notify_url"] = $this->notify_url;
  138. $orderParam["valid_time"] = $this->valid_time;
  139. $orderParam["app_id"] = $this->app_id;
  140. if($phone_type == 'ios'){
  141. // ios唤起支付url需要
  142. $orderParam["pay_scene"] = 'LinkPay';
  143. }
  144. $data = json_encode(["sign" => $this->sign($orderParam)] + $orderParam);
  145. \Log::info('applyOrderCreate_before_request:'.json_encode($data));
  146. $this->orderParam = json_decode($this->curl_post($this->payUrl, $data), true);
  147. return $this->orderParam;
  148. }
  149. /**
  150. * 申请退款
  151. *
  152. */
  153. public function applyOrderRefund2($order)
  154. {
  155. $order['notify_url'] = $this->refund_notify_url;
  156. $order['app_id'] = $this->app_id;
  157. return json_decode($this->curl_post($this->refundUrl, json_encode(['sign' => $this->sign($order)] + $order)), true);
  158. }
  159. /**
  160. * 订单查询
  161. * @param string $out_order_no 订单号
  162. * @return array 订单信息
  163. */
  164. public function findOrder($out_order_no)
  165. {
  166. if (empty($out_order_no)) {
  167. return false;
  168. }
  169. $order = [
  170. 'out_order_no' => $out_order_no,
  171. 'app_id' => $this->app_id,
  172. ];
  173. $order['sign'] = $this->sign($order);
  174. return json_decode($this->curl_post($this->query, json_encode($order)), true);
  175. }
  176. /**
  177. * 分账
  178. *
  179. * @param [type] $order
  180. * @return void
  181. * @author LiJie
  182. */
  183. public function settle($order)
  184. {
  185. $data = [
  186. 'app_id' => $this->app_id,
  187. 'out_settle_no' => $order['out_settle_no'],
  188. 'out_order_no' => $order['out_order_no'],
  189. 'settle_desc' => $order['settle_desc'],
  190. 'notify_url' => $this->settle_url,
  191. 'cp_extra' => $order['cp_extra'],
  192. ];
  193. $data['sign'] = $this->sign($data);
  194. $result = json_decode($this->curl_post($this->settle, json_encode($data)), true);
  195. return $result;
  196. }
  197. /**
  198. * 退款
  199. * @return mixed
  200. */
  201. public function applyOrderRefund($order){
  202. $orderParam["out_order_no"] = $order['out_order_no'];
  203. $orderParam["out_refund_no"] = $order['out_refund_no'];
  204. $orderParam["reason"] = $order['reason'];
  205. $orderParam["refund_amount"] = $order['refund_amount'];
  206. // $orderParam["cp_extra"] = $order['cp_extra'];
  207. $orderParam["notify_url"] = $order['notify_url'];
  208. $orderParam["app_id"] = $this->app_id;
  209. $data = json_encode(["sign" => $this->sign_gf($orderParam)] + $orderParam);
  210. \LOg::info('applyOrderRefund_request:'.json_encode($data));
  211. $this->refundResult = json_decode($this->curl_post($this->refundUrl, $data), true);
  212. return $this->refundResult;
  213. }
  214. /**
  215. * 发送模版消息
  216. *
  217. * @param [type] $data
  218. * @param [type] $token
  219. */
  220. public function sendMsg($data, $token)
  221. {
  222. $data['access_token'] = $token;
  223. $data['app_id'] = $this->app_id;
  224. return json_decode($this->curl_post($this->sendMsgUrl, json_encode($data)), true);
  225. }
  226. /**
  227. * 解密手机号
  228. *
  229. * @param string $session_key 前端传递的session_key
  230. * @param string $iv 前端传递的iv
  231. * @param string $encryptedData 前端传递的encryptedData
  232. */
  233. public function decryptPhone($session_key, $iv, $encryptedData)
  234. {
  235. if (strlen($session_key) != 24) {
  236. return false;
  237. }
  238. $aesKey = base64_decode($session_key);
  239. if (strlen($iv) != 24) {
  240. return false;
  241. }
  242. $aesIV = base64_decode($iv);
  243. $aesCipher = base64_decode($encryptedData);
  244. $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
  245. $dataObj = json_decode($result);
  246. if ($dataObj == null) {
  247. return false;
  248. }
  249. if ($dataObj->watermark->appid != $this->app_id) {
  250. return false;
  251. }
  252. return json_decode($result, true);
  253. }
  254. protected static function curl_get($url)
  255. {
  256. $headerArr = array("Content-type:application/x-www-form-urlencoded");
  257. $ch = curl_init();
  258. curl_setopt($ch, CURLOPT_URL, $url);
  259. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  260. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  261. curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
  262. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  263. curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  264. $output = curl_exec($ch);
  265. if (!$output) {
  266. throw new \Exception(curl_errno($ch));
  267. }
  268. curl_close($ch);
  269. return $output;
  270. }
  271. /**
  272. * @desc post 用于退款
  273. */
  274. protected static function curl_post($url, $data)
  275. {
  276. $ch = curl_init();
  277. curl_setopt($ch, CURLOPT_URL, $url);
  278. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  279. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  280. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  281. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  282. curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  283. curl_setopt(
  284. $ch,
  285. CURLOPT_HTTPHEADER,
  286. array(
  287. 'Content-Type: application/json;charset=utf-8',
  288. 'Content-Length: ' . strlen($data),
  289. )
  290. );
  291. curl_setopt($ch, CURLOPT_POST, 1);
  292. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  293. $output = curl_exec($ch);
  294. if (!$output) {
  295. throw new \Exception(curl_errno($ch));
  296. }
  297. curl_close($ch);
  298. return $output;
  299. }
  300. /**
  301. * @param array $map
  302. * @return string
  303. */
  304. public function sign(array $map)
  305. {
  306. $rList = array();
  307. foreach ($map as $k => $v) {
  308. if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id") {
  309. continue;
  310. }
  311. $value = trim(strval($v));
  312. $len = strlen($value);
  313. if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"") {
  314. $value = substr($value, 1, $len - 1);
  315. }
  316. $value = trim($value);
  317. if ($value == "" || $value == "null") {
  318. continue;
  319. }
  320. array_push($rList, $value);
  321. }
  322. array_push($rList, $this->salt);
  323. sort($rList, 2);
  324. return md5(implode('&', $rList));
  325. }
  326. // 官方签名
  327. public function sign_gf($map) {
  328. $rList = [];
  329. foreach($map as $k =>$v) {
  330. if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id")
  331. continue;
  332. $value = trim(strval($v));
  333. if (is_array($v)) {
  334. $value = arrayToStr($v);
  335. }
  336. $len = strlen($value);
  337. if ($len > 1 && substr($value, 0,1)=="\"" && substr($value, $len-1)=="\"")
  338. $value = substr($value,1, $len-1);
  339. $value = trim($value);
  340. if ($value == "" || $value == "null")
  341. continue;
  342. $rList[] = $value;
  343. }
  344. $rList[] = $this->salt;
  345. sort($rList, SORT_STRING);
  346. return md5(implode('&', $rList));
  347. }
  348. }