Allinpay.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. namespace app\utils\Allinpay;
  3. use app\models\Option;
  4. use app\constants\OptionSetting;
  5. use app\models\Order;
  6. use app\models\OrderUnion;
  7. use app\models\SaasUser;
  8. use app\utils\OrderNo;
  9. use app\models\AccountLog;
  10. use app\modules\admin\models\SaasForm;
  11. use app\models\SaasDistribution;
  12. use app\utils\Wechat\WechatMini;
  13. use app\utils\Alipay\Alipay;
  14. use app\utils\Allinpay\AppUtil;
  15. class Allinpay {
  16. public static $api_host_url = 'https://vsp.allinpay.com/';
  17. // public static $api_host_url = 'https://syb-test.allinpay.com/';
  18. public static $notify_url = 'allinpay/notify';
  19. public static $h5_url = 'order/order/order';
  20. public static $confs = [];
  21. public static function conf($store_id = 0, $refresh = 0) {
  22. if (isset(self::$confs[$store_id]) && !$refresh) {
  23. return self::$confs[$store_id];
  24. }
  25. $confDef = [
  26. "cusid" => "",
  27. "appid" => "",
  28. "rsa_private_key" => "",
  29. "rsa_public_key" => "",
  30. 'app_alipay_mini_id' => '',
  31. 'app_wx_gh_id' => '',
  32. 'app_wx_gh_id_type' => 0,
  33. 'wx_other_mini' => 0,
  34. 'wx_other_mini_appId' => 'wxef277996acc166c3', //https://aipboss.allinpay.com/know/devhelp/main.php?pid=38#mid=1050
  35. ];
  36. $conf = Option::get(OptionSetting::ALLINPAY, $store_id, 'store')['value'];
  37. if ($conf) {
  38. $conf = array_merge($confDef, json_decode($conf, true));
  39. } else {
  40. $conf = $confDef;
  41. }
  42. self::$confs[$store_id] = $conf;
  43. return $conf;
  44. }
  45. public static function saveConf($store_id = 0, $config = []) {
  46. $oldConf = self::conf($store_id, 1);
  47. $conf = array_merge($oldConf, $config);
  48. $set = Option::set(OptionSetting::ALLINPAY, json_encode($conf), $store_id, 'store');
  49. self::conf($store_id, 1);
  50. return $set;
  51. }
  52. public static function request($url, $params) {
  53. $ch = curl_init();
  54. $this_header = array("content-type: application/x-www-form-urlencoded;charset=UTF-8");
  55. curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
  56. curl_setopt($ch, CURLOPT_URL, $url);
  57. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  58. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
  59. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  60. curl_setopt($ch, CURLOPT_POST, 1);
  61. curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  62. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); //如果不加验证,就设false,商户自行处理
  63. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
  64. $output = curl_exec($ch);
  65. curl_close($ch);
  66. return $output;
  67. }
  68. public static function apiData($store_id, $params) {
  69. $conf = self::conf($store_id);
  70. $params["cusid"] = $conf['cusid'];
  71. $params["appid"] = $conf['appid'];
  72. // $params["version"] = '11';
  73. $params["randomstr"] = microtime(true);
  74. $params["signtype"] = 'RSA';
  75. $params["sign"] = urlencode(AppUtil::Sign($params, $conf['rsa_private_key'])); //签名
  76. debug_log([$params["randomstr"], "请求参数:", $params], __CLASS__ . '.log');
  77. return $params;
  78. }
  79. public static function api($store_id, $path, $params) {
  80. $conf = self::conf($store_id);
  81. $params = self::apiData($store_id, $params);
  82. $paramsStr = AppUtil::ToUrlParams($params);
  83. $url = self::$api_host_url . $path;
  84. $rsp = self::request($url, $paramsStr);
  85. $rspArray = json_decode($rsp, true);
  86. debug_log([$params["randomstr"], "请求返回:", $rspArray, $rsp], __CLASS__ . '.log');
  87. if($rspArray['retcode'] !== 'SUCCESS'){
  88. return [
  89. 'code' => 1,
  90. 'msg' => $rspArray['retmsg'],
  91. 'data' => $rspArray,
  92. ];
  93. }
  94. if ($conf['rsa_public_key'] && !AppUtil::validSign($rspArray, $conf['rsa_public_key'])) {
  95. debug_log([$params["randomstr"], "验签错误:"], __CLASS__ . '.log');
  96. return [
  97. 'code' => 1,
  98. 'msg' => '验签错误。' . $rspArray['errmsg'],
  99. 'data' => $rspArray,
  100. ];
  101. }
  102. return [
  103. 'code' => 0,
  104. 'msg' => 'ok',
  105. 'data' => $rspArray,
  106. ];
  107. }
  108. public static function pay($pay_type, $order, $type, $goods_names = null, $total_pay_price = 0, $is_app = false, $balance_price = 0, $is_h5 = false, $is_official = false) {
  109. if (!$order || !in_array($type, OrderNo::$validOrderType) || ($type != OrderNo::ORDER_UNION && !$goods_names)) {
  110. return [
  111. 'code' => 1,
  112. 'msg' => '订单信息或订单类型错误'
  113. ];
  114. }
  115. $store_id = $order->store_id;
  116. $conf = self::conf($store_id);
  117. $sub_appid = '';
  118. $open_id = '';
  119. if($pay_type == Order::PAY_TYPE_ALLINPAY_WX){
  120. if ($is_app) {
  121. $open_id = get_user()->wechat_app_open_id;
  122. } else if ($is_official) {
  123. $open_id = get_user()->wechat_platform_open_id;
  124. } else {
  125. $open_id = get_user()->wechat_open_id;
  126. }
  127. $mini = WechatMini::getWechatConfig($store_id);
  128. $mini && $sub_appid = $mini->getConfig()['app_id'];
  129. }
  130. if($pay_type == Order::PAY_TYPE_ALLINPAY_ALIPAY){
  131. $open_id = get_user()->alipay_open_id;
  132. Alipay::init();
  133. Alipay::$alipay_conf && $sub_appid = Alipay::$alipay_conf['app_id'];
  134. }
  135. if ($type == OrderNo::ORDER_UNION) {
  136. $goods_title = count($order) . '笔订单合并支付';
  137. $out_trade_no = OrderNo::getOrderNo(OrderNo::ORDER_UNION);
  138. $total_fee = $balance_price > 0 ? floatval($total_pay_price - $balance_price) * 100 : $total_pay_price * 100;
  139. $order_union = new OrderUnion();
  140. $order_union->store_id = get_store_id();
  141. $order_union->user_id = get_user()->id;
  142. $order_union->order_no = $out_trade_no;
  143. $order_union->price = $total_pay_price;
  144. $order_union->is_pay = 0;
  145. $order_union->created_at = time();
  146. $order_union->is_delete = 0;
  147. $order_id_list = [];
  148. foreach ($order as $value) {
  149. $order_id_list[] = $value->id;
  150. }
  151. $order_union->order_id_list = json_encode($order_id_list);
  152. if (!$order_union->save()) {
  153. foreach ($order_union->errors as $error) {
  154. return [
  155. 'code' => 1,
  156. 'msg' => $error
  157. ];
  158. }
  159. }
  160. } else {
  161. $pay_price = $order->pay_price;
  162. // if ($type != OrderNo::ORDER_RECHARGE) {
  163. // if (isset($order->is_delivery) && $order->is_delivery == 1) {
  164. // $deliveryInfo = DeliveryInfo::find()->where(['order_no' => $order->order_no])->one();
  165. // if ($deliveryInfo) {
  166. // $pay_price += $deliveryInfo->fee;
  167. // }
  168. // }
  169. // }
  170. $goods_title = mb_substr($goods_names, 0, 20);
  171. $out_trade_no = $order->order_no;
  172. $total_fee = $balance_price > 0 ? floatval($pay_price - $balance_price) * 100 : $pay_price * 100;
  173. }
  174. # 支付设置
  175. $payment_params = array(
  176. "trxamt"=> $total_fee,
  177. // "version"=> 11,
  178. // "reqsn"=> $out_trade_no,
  179. "unireqsn"=> $out_trade_no,
  180. // "paytype"=> $pay_channel,
  181. "body"=> $goods_title,
  182. 'acct' => $open_id,
  183. 'notify_url' => pay_notify_url(self::$notify_url),
  184. // 'sub_appid' => $sub_appid,
  185. );
  186. if($pay_type == Order::PAY_TYPE_ALLINPAY_WX){
  187. if ($is_h5) {
  188. $pay_channel = 'W02';
  189. $payment_params['front_url'] = pay_notify_url(self::$h5_url);
  190. } else if ($is_app) {
  191. $pay_channel = 'W03';
  192. } else {
  193. $pay_channel = 'W06';
  194. }
  195. }
  196. if($pay_type == Order::PAY_TYPE_ALLINPAY_ALIPAY){
  197. $pay_channel = 'A01';
  198. }
  199. $payment_params['paytype'] = $pay_channel;
  200. // // 是否走分账
  201. // $pay_mode = '';
  202. // if (is_profit_sharing()) {
  203. // $pay_mode = 'delay';
  204. // }
  205. // //强制走分账流程,减少判断逻辑
  206. // $pay_mode = 'delay';
  207. if($order->allinpay_payment_id){
  208. $info = self::api($store_id, 'apiweb/tranx/query', ['trxid' => $order->allinpay_payment_id]);
  209. if ($info['code']){
  210. return $info;
  211. } else {
  212. $info = $info['data'];
  213. if($info['trxstatus'] === '0000'){
  214. return [
  215. 'code' => 1,
  216. 'msg' => '支付状态异常,已支付',
  217. ];
  218. }
  219. }
  220. }
  221. if($conf['wx_other_mini']){
  222. $payment_params['version'] = 12;
  223. $return = [
  224. 'code' => 0,
  225. 'msg' => 'success',
  226. 'wx_other_mini' => $conf['wx_other_mini'],
  227. 'wx_other_mini_appId' => $conf['wx_other_mini_appId'],
  228. 'res' => self::apiData($store_id, $payment_params),
  229. 'data' => $payment_params,
  230. '$payment_params' => $payment_params,
  231. ];
  232. $return['order_no'] = $payment_params['out_trade_no'];
  233. $return['body'] = $goods_title;
  234. return $return;
  235. }
  236. $pay = self::api($store_id, 'apiweb/unitorder/pay', $payment_params);
  237. # 对支付结果进行处理
  238. if ($pay['code']){
  239. return $pay;
  240. } else {
  241. $pay = $pay['data'];
  242. if($pay['trxstatus'] !== '0000'){
  243. return [
  244. 'code' => 1,
  245. 'msg' => '交易失败1,' . (string)$pay['errmsg'],
  246. ];
  247. }
  248. $payinfo = $pay['payinfo'];
  249. if($pay_type == Order::PAY_TYPE_ALLINPAY_WX){
  250. $payinfo = json_decode($pay['payinfo'], true);
  251. }
  252. //成功处理
  253. $return = [
  254. 'code' => 0,
  255. 'msg' => 'success',
  256. 'res' => $pay,
  257. 'data' => $payinfo,
  258. '$payment_params' => $payment_params,
  259. ];
  260. if ($type == OrderNo::ORDER_UNION) {
  261. foreach ($order as $value) {
  262. $value->order_union_id = $value->id;
  263. $value->allinpay_payment_id = $pay['trxid'];
  264. $value->allinpay_trxcode = $pay['trxcode'];
  265. $value->save();
  266. }
  267. $return['order_no'] = $payment_params['out_trade_no'];
  268. $return['body'] = $goods_title;
  269. }else{
  270. $order->allinpay_payment_id = $pay['trxid'];
  271. $order->allinpay_trxcode = $pay['trxcode'];
  272. $order->save();
  273. }
  274. if ($is_h5) {
  275. $return['res']['mweb_url'] = $pay['payinfo'] . '&redirect_url=' . \Yii::$app->request->hostInfo . '/h5/#/order/order/order';
  276. }
  277. return $return;
  278. }
  279. }
  280. /**
  281. * 退款
  282. * @param Object $order
  283. * @param string $orderRefundNo
  284. * @param string $type
  285. * @param integer $refundFee
  286. * @param $refund_account
  287. * @return array
  288. */
  289. public static function orderRefund($order, $type, $refundFee, $orderRefundNo, $refund_account = null) {
  290. $store_id = $order->store_id;
  291. $info = self::api($store_id, 'apiweb/tranx/query', ['trxid' => $order->allinpay_payment_id]);
  292. if ($info['code']){
  293. return $info;
  294. } else {
  295. $info = $info['data'];
  296. if($info['trxstatus'] !== '0000'){
  297. return [
  298. 'code' => 1,
  299. 'msg' => '支付状态异常,未支付',
  300. ];
  301. }
  302. }
  303. $refund_params = [
  304. "oldtrxid"=> $order->allinpay_payment_id,
  305. "reqsn"=> $orderRefundNo,
  306. "trxamt"=> $refundFee * 100,
  307. "remark"=> "退款",
  308. ];
  309. # 发起退款
  310. $refund = self::refund($store_id, $refund_params);
  311. // 联合支付,退余额
  312. if ($order->is_combine_pay == 1 && $order->combine_money > 0) {
  313. AccountLog::saveLog($order->user_id, $order->combine_money, AccountLog::TYPE_BALANCE, AccountLog::LOG_TYPE_INCOME, AccountLog::TYPE_PLATFORM_REFUND_ORDER, $order->id, "商城订单退款,订单号:{$order->order_no}");
  314. }
  315. return [
  316. 'code' => 0,
  317. 'msg' => 'success',
  318. 'data' => $refund->result,
  319. ];
  320. }
  321. public static function refund($store_id, $refund_params) {
  322. # 发起撤销
  323. $cancel = self::api($store_id, 'apiweb/tranx/cancel', $refund_params);
  324. if (!$cancel['code']){
  325. $cancel = $cancel['data'];
  326. if($cancel['trxstatus'] === '0000'){
  327. return [
  328. 'code' => 0,
  329. 'msg' => '退款成功。',
  330. ];
  331. }
  332. }
  333. # 发起退款
  334. $refund = self::api($store_id, 'apiweb/tranx/refund', $refund_params);
  335. if (!$refund['code']){
  336. $refund = $refund['data'];
  337. if($refund['trxstatus'] !== '0000'){
  338. return [
  339. 'code' => 1,
  340. 'msg' => '退款成功。',
  341. ];
  342. }
  343. }
  344. return [
  345. 'code' => 1,
  346. 'msg' => '退款失败1,' . (string)$cancel['errmsg'] . ';' . (string)$refund['errmsg'],
  347. ];
  348. }
  349. }