PaymentService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <?php
  2. namespace app\data\service;
  3. use app\data\model\BaseUserPayment;
  4. use app\data\model\DataUser;
  5. use app\data\model\DataUserPayment;
  6. use app\data\model\DataUserVipOrder;
  7. use app\data\model\ShopGoods;
  8. use app\data\model\ShopOrder;
  9. use app\data\model\ShopOrderItem;
  10. use app\data\model\ShopOrderPay;
  11. use app\data\model\ShopGoodsItem;
  12. use app\data\service\payment\AlipayPaymentService;
  13. use app\data\service\payment\BalancePaymentService;
  14. use app\data\service\payment\EmptyPaymentService;
  15. use app\data\service\payment\JoinpayPaymentService;
  16. use app\data\service\payment\VoucherPaymentService;
  17. use app\data\service\payment\WechatPaymentService;
  18. use think\admin\Exception;
  19. use think\admin\Library;
  20. use think\App;
  21. use app\data\model\SystemUserMessage;
  22. /**
  23. * 支付基础服务
  24. * Class PaymentService
  25. * @package app\data\service
  26. */
  27. abstract class PaymentService
  28. {
  29. // 用户余额支付
  30. const PAYMENT_EMPTY = 'empty';
  31. const PAYMENT_BALANCE = 'balance';
  32. const PAYMENT_VOUCHER = 'voucher';
  33. // 汇聚支付参数
  34. const PAYMENT_JOINPAY_GZH = 'joinpay_gzh';
  35. const PAYMENT_JOINPAY_XCX = 'joinpay_xcx';
  36. // 微信商户支付
  37. const PAYMENT_WECHAT_APP = 'wechat_app';
  38. const PAYMENT_WECHAT_GZH = 'wechat_gzh';
  39. const PAYMENT_WECHAT_XCX = 'wechat_xcx';
  40. const PAYMENT_WECHAT_WAP = 'wechat_wap';
  41. const PAYMENT_WECHAT_QRC = 'wechat_qrc';
  42. // 支付宝支付参数
  43. const PAYMENT_ALIAPY_APP = 'alipay_app';
  44. const PAYMENT_ALIPAY_WAP = 'alipay_wap';
  45. const PAYMENT_ALIPAY_WEB = 'alipay_web';
  46. // 支付通道配置,不需要的可以注释
  47. const TYPES = [
  48. // 空支付,金额为零时自动完成支付
  49. self::PAYMENT_EMPTY => [
  50. 'type' => 'EMPTY',
  51. 'name' => '订单无需支付',
  52. 'bind' => [],
  53. ],
  54. // 余额支付,使用账号余额完成支付
  55. self::PAYMENT_BALANCE => [
  56. 'type' => 'BALANCE',
  57. 'name' => '账号余额支付',
  58. 'bind' => [
  59. UserAdminService::API_TYPE_WAP, UserAdminService::API_TYPE_WEB,
  60. UserAdminService::API_TYPE_WXAPP, UserAdminService::API_TYPE_WECHAT,
  61. UserAdminService::API_TYPE_IOSAPP, UserAdminService::API_TYPE_ANDROID,
  62. ],
  63. ],
  64. // 凭证支付,上传凭证后台审核支付
  65. self::PAYMENT_VOUCHER => [
  66. 'type' => 'VOUCHER',
  67. 'name' => '单据凭证支付',
  68. 'bind' => [
  69. UserAdminService::API_TYPE_WAP, UserAdminService::API_TYPE_WEB,
  70. UserAdminService::API_TYPE_WXAPP, UserAdminService::API_TYPE_WECHAT,
  71. UserAdminService::API_TYPE_IOSAPP, UserAdminService::API_TYPE_ANDROID,
  72. ],
  73. ],
  74. // 微信支付配置(不需要的直接注释)
  75. self::PAYMENT_WECHAT_WAP => [
  76. 'type' => 'MWEB',
  77. 'name' => '微信WAP支付',
  78. 'bind' => [UserAdminService::API_TYPE_WAP],
  79. ],
  80. self::PAYMENT_WECHAT_APP => [
  81. 'type' => 'APP',
  82. 'name' => '微信APP支付',
  83. 'bind' => [UserAdminService::API_TYPE_IOSAPP, UserAdminService::API_TYPE_ANDROID],
  84. ],
  85. self::PAYMENT_WECHAT_XCX => [
  86. 'type' => 'JSAPI',
  87. 'name' => '微信小程序支付',
  88. 'bind' => [UserAdminService::API_TYPE_WXAPP],
  89. ],
  90. self::PAYMENT_WECHAT_GZH => [
  91. 'type' => 'JSAPI',
  92. 'name' => '微信公众号支付',
  93. 'bind' => [UserAdminService::API_TYPE_WECHAT],
  94. ],
  95. self::PAYMENT_WECHAT_QRC => [
  96. 'type' => 'NATIVE',
  97. 'name' => '微信二维码支付',
  98. 'bind' => [UserAdminService::API_TYPE_WEB],
  99. ],
  100. // 支付宝支持配置(不需要的直接注释)
  101. self::PAYMENT_ALIPAY_WAP => [
  102. 'type' => '',
  103. 'name' => '支付宝WAP支付',
  104. 'bind' => [UserAdminService::API_TYPE_WAP],
  105. ],
  106. self::PAYMENT_ALIPAY_WEB => [
  107. 'type' => '',
  108. 'name' => '支付宝WEB支付',
  109. 'bind' => [UserAdminService::API_TYPE_WEB],
  110. ],
  111. self::PAYMENT_ALIAPY_APP => [
  112. 'type' => 'alipay_app',
  113. 'name' => '支付宝APP支付',
  114. 'bind' => [UserAdminService::API_TYPE_ANDROID, UserAdminService::API_TYPE_IOSAPP],
  115. ],
  116. // 汇聚支持配置(不需要的直接注释)
  117. self::PAYMENT_JOINPAY_XCX => [
  118. 'type' => 'WEIXIN_XCX',
  119. 'name' => '汇聚小程序支付',
  120. 'bind' => [UserAdminService::API_TYPE_WXAPP],
  121. ],
  122. self::PAYMENT_JOINPAY_GZH => [
  123. 'type' => 'WEIXIN_GZH',
  124. 'name' => '汇聚公众号支付',
  125. 'bind' => [UserAdminService::API_TYPE_WECHAT],
  126. ],
  127. ];
  128. /**
  129. * 支付服务对象
  130. * @var array
  131. */
  132. protected static $driver = [];
  133. /**
  134. * 当前应用
  135. * @var App
  136. */
  137. protected $app;
  138. /**
  139. * 支付参数编号
  140. * @var string
  141. */
  142. protected $code;
  143. /**
  144. * 默认支付类型
  145. * @var string
  146. */
  147. protected $type;
  148. /**
  149. * 当前支付参数
  150. * @var array
  151. */
  152. protected $params;
  153. /**
  154. * PaymentService constructor.
  155. * @param App $app 当前应用对象
  156. * @param string $code 支付参数编号
  157. * @param string $type 支付类型代码
  158. * @param array $params 支付参数配置
  159. */
  160. public function __construct(App $app, string $code, string $type, array $params)
  161. {
  162. [$this->app, $this->code, $this->type, $this->params] = [$app, $code, $type, $params];
  163. if (method_exists($this, 'initialize')) $this->initialize();
  164. }
  165. /**
  166. * 根据配置实例支付服务
  167. * @param string $code 支付配置编号
  168. * @return PaymentService
  169. * @throws \think\admin\Exception
  170. */
  171. public static function instance(string $code): PaymentService
  172. {
  173. if ($code === 'empty') {
  174. $vars = ['code' => 'empty', 'type' => 'empty', 'params' => []];
  175. return static::$driver[$code] = Library::$sapp->make(EmptyPaymentService::class, $vars);
  176. }
  177. [, $type, $params] = self::config($code);
  178. if (isset(static::$driver[$code])) return static::$driver[$code];
  179. $vars = ['code' => $code, 'type' => $type, 'params' => $params];
  180. // 实例化具体支付参数类型
  181. if (stripos($type, 'balance') === 0) {
  182. return static::$driver[$code] = Library::$sapp->make(BalancePaymentService::class, $vars);
  183. } elseif (stripos($type, 'voucher') === 0) {
  184. return static::$driver[$code] = Library::$sapp->make(VoucherPaymentService::class, $vars);
  185. } elseif (stripos($type, 'alipay_') === 0) {
  186. return static::$driver[$code] = Library::$sapp->make(AlipayPaymentService::class, $vars);
  187. } elseif (stripos($type, 'wechat_') === 0) {
  188. return static::$driver[$code] = Library::$sapp->make(WechatPaymentService::class, $vars);
  189. } elseif (stripos($type, 'joinpay_') === 0) {
  190. return static::$driver[$code] = Library::$sapp->make(JoinpayPaymentService::class, $vars);
  191. } else {
  192. throw new Exception(sprintf('支付驱动[%s]未定义', $type));
  193. }
  194. }
  195. /**
  196. * 获取支付配置参数
  197. * @param string $code
  198. * @param array $payment
  199. * @return array [code, type, params]
  200. * @throws Exception
  201. */
  202. public static function config(string $code, array $payment = []): array
  203. {
  204. try {
  205. if (empty($payment)) {
  206. $map = ['code' => $code, 'status' => 1, 'deleted' => 0];
  207. $payment = BaseUserPayment::mk()->where($map)->find();
  208. }
  209. if (empty($payment)) {
  210. throw new Exception("支付参数[#{$code}]禁用关闭");
  211. }
  212. $params = @json_decode($payment['content'], true);
  213. if (empty($params)) {
  214. throw new Exception("支付参数[#{$code}]配置无效");
  215. }
  216. if (empty(static::TYPES[$payment['type']])) {
  217. throw new Exception("支付参数[@{$payment['type']}]匹配失败");
  218. }
  219. return [$payment['code'], $payment['type'], $params];
  220. } catch (\Exception $exception) {
  221. throw new Exception($exception->getMessage(), $exception->getCode());
  222. }
  223. }
  224. /**
  225. * 获取支付支付名称
  226. * @param string $type
  227. * @return string
  228. */
  229. public static function name(string $type): string
  230. {
  231. return static::TYPES[$type]['name'] ?? $type;
  232. }
  233. /**
  234. * 获取支付类型
  235. * @param array $types 默认返回支付
  236. * @return array
  237. */
  238. public static function getTypeAll(array $types = []): array
  239. {
  240. $binds = array_keys(UserAdminService::TYPES);
  241. foreach (static::TYPES as $k => $v) if (isset($v['bind'])) {
  242. if (array_intersect($v['bind'], $binds)) $types[$k] = $v;
  243. }
  244. return $types;
  245. }
  246. /**
  247. * 筛选可用的支付类型
  248. * @param string $api 指定接口类型
  249. * @param array $types 默认返回支付
  250. * @return array
  251. */
  252. public static function getTypeApi(string $api = '', array $types = []): array
  253. {
  254. foreach (self::TYPES as $type => $attr) {
  255. if (in_array($api, $attr['bind'])) $types[] = $type;
  256. }
  257. return array_unique($types);
  258. }
  259. /**
  260. * 订单主动查询
  261. * @param string $orderNo
  262. * @return array
  263. */
  264. abstract public function query(string $orderNo): array;
  265. /**
  266. * 支付通知处理
  267. * @return string
  268. */
  269. abstract public function opvip(): string;
  270. /**
  271. * 支付通知处理
  272. * @return string
  273. */
  274. abstract public function payorder(): string;
  275. /**
  276. * 创建支付订单
  277. * @param string $openid 用户OPENID
  278. * @param string $orderNo 交易订单单号
  279. * @param string $payAmount 交易订单金额(元)
  280. * @param string $payTitle 交易订单名称
  281. * @param string $payRemark 交易订单描述
  282. * @param string $payReturn 支付回跳地址
  283. * @param string $payImage 支付凭证图片
  284. * @return array
  285. */
  286. abstract public function create(string $openid, string $orderNo, string $payAmount, string $payTitle, string $payRemark, string $payReturn = '', string $payImage = '',string $notify_url = '',array $parm = []): array;
  287. /**
  288. * 创建支付行为
  289. * @param string $orderNo 商户订单单号
  290. * @param string $payTitle 商户订单标题
  291. * @param string $payAmount 需要支付金额
  292. */
  293. protected function createPaymentAction(string $orderNo, string $payTitle, string $payAmount)
  294. {
  295. DataUserPayment::mk()->insert([
  296. 'payment_code' => $this->code,
  297. 'payment_type' => $this->type,
  298. 'order_no' => $orderNo,
  299. 'order_name' => $payTitle,
  300. 'order_amount' => $payAmount,
  301. ]);
  302. }
  303. /**
  304. * 更新开通vip会员订单
  305. * @return boolean
  306. */
  307. protected function updateOpvipAction(array $notify): bool
  308. {
  309. $orderNo = $notify['out_trade_no'];
  310. $order = DataUserVipOrder::mk()->where('order_no',$orderNo)->findOrEmpty();
  311. if ($order->isEmpty()) return false;
  312. if ($order['status']) return false;
  313. if ($order->pay_type=='balance'){
  314. //余额支付
  315. $resu = userMoneyChange($order->yue_price,1,$order->uuid,'开通vip',0,$order->id);
  316. if (!$resu) return false;
  317. }
  318. $user = DataUser::mk()->where('id',$order->uuid)->findOrEmpty();
  319. DataUser::mk()->where('id',$order->uuid)->update(
  320. [
  321. 'is_vip'=>1,
  322. 'vip_name'=>'船白知会员',
  323. 'vip_order'=>$order->id,
  324. 'vip_datetime'=> $user->is_vip ? date('Y-m-d H:i:s',strtotime($user->vip_datetime)+(365*24*60*60)) : date('Y-m-d H:i:s',time()+(365*24*60*60))
  325. ]
  326. );
  327. // 更新支付记录
  328. $order->status = 1;
  329. $order->pay_at = date('Y-m-d H:i:s');
  330. $order->pay_return = json_encode($notify,true);
  331. $order->save();
  332. // 更新记录状态
  333. return true;
  334. }
  335. /**
  336. * 更新开通vip会员订单
  337. * @return boolean
  338. */
  339. protected function updatePayorderAction(array $notify): bool
  340. {
  341. $orderNo = $notify['out_trade_no'];
  342. $order = ShopOrderPay::mk()->where('pay_no',$orderNo)->findOrEmpty();
  343. if ($order->isEmpty()) return false;
  344. $order_nos = ShopOrder::mk()->where(['payment_trade'=>$orderNo])->field('order_no')->select();
  345. foreach ($order_nos as &$v){
  346. $info = ShopOrderItem::mk()->where('order_no',$v['order_no'])->field('goods_code,goods_sku,stock_sales')->find();
  347. $goods = ShopGoods::mk()->where('code',$info['goods_code'])->findOrEmpty();
  348. $item = ShopGoodsItem::mk()->where('goods_sku',$info['goods_sku'])->findOrEmpty();
  349. $goods->stock_total -=$info['stock_sales'];
  350. $goods->stock_sales +=$info['stock_sales'];
  351. $item->stock_total -=$info['stock_sales'];
  352. $goods->save();
  353. $item->save();
  354. }
  355. ShopOrder::mk()->where(['payment_trade'=>$orderNo])->save(['status'=>4,'payment_datetime'=>date('Y-m-d H:i:s'),'payment_status'=>1]);
  356. //商家消息提醒
  357. $order_info = ShopOrder::mk()->where(['payment_trade'=>$orderNo])->field('order_no,admin_id')->find();
  358. $mes = ShopOrderItem::mk()->where('order_no',$order_info['order_no'])->find();
  359. $data = [
  360. 'type'=>2,
  361. 'uuid'=>$order_info['admin_id'],
  362. 'title'=>'买家已经付款',
  363. 'content'=>$mes['goods_name'],
  364. 'image'=>$mes['goods_cover'],
  365. 'remark'=>'买家已经付款,请尽快发货哦~'
  366. ];
  367. $shop_order_model = new SystemUserMessage();
  368. $shop_order_model->systemMessageadd($data);
  369. // 更新支付记录
  370. $order->status = 2;
  371. $order->pay_at = date('Y-m-d H:i:s');
  372. $order->transaction_id = json_encode($notify,true);
  373. $order->save();
  374. // 更新记录状态
  375. return true;
  376. }
  377. /**
  378. * 订单支付更新操作
  379. * @param string $orderNo 订单单号
  380. * @param string $payTrade 交易单号
  381. * @param string $payAmount 支付金额
  382. * @param string $payRemark 支付描述
  383. * @param string $payImage 支付凭证
  384. * @return boolean
  385. */
  386. protected function updatePaymentOrder(string $orderNo, string $payTrade, string $payAmount, string $payRemark = '在线支付', string $payImage = ''): bool
  387. {
  388. $map = ['status' => 2, 'order_no' => $orderNo, 'payment_status' => 0];
  389. $order = ShopOrder::mk()->where($map)->findOrEmpty();
  390. if ($order->isEmpty()) return false;
  391. // 检查订单支付状态
  392. if ($this->type === self::PAYMENT_VOUCHER) {
  393. $status = 3; # 凭证支付需要审核
  394. } elseif (empty($order['truck_type'])) {
  395. $status = 6; # 虚拟订单直接完成
  396. } else {
  397. $status = 4; # 实物订单需要发货
  398. }
  399. // 更新订单支付状态
  400. $order['status'] = $status;
  401. $order['payment_code'] = $this->code;
  402. $order['payment_type'] = $this->type;
  403. $order['payment_trade'] = $payTrade;
  404. $order['payment_image'] = $payImage;
  405. $order['payment_amount'] = $payAmount;
  406. $order['payment_remark'] = $payRemark;
  407. $order['payment_status'] = 1;
  408. $order['payment_datetime'] = date('Y-m-d H:i:s');
  409. $order->save();
  410. // 触发订单更新事件
  411. if ($status >= 4) {
  412. $this->app->event->trigger('ShopOrderPayment', $orderNo);
  413. }
  414. return true;
  415. }
  416. }