PaymentService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <?php
  2. namespace app\data\service;
  3. use app\data\model\BaseUserPayment;
  4. use app\data\model\DataUserPayment;
  5. use app\data\model\ShopOrder;
  6. use app\data\service\payment\AlipayPaymentService;
  7. use app\data\service\payment\BalancePyamentService;
  8. use app\data\service\payment\EmptyPaymentService;
  9. use app\data\service\payment\JoinpayPaymentService;
  10. use app\data\service\payment\VoucherPaymentService;
  11. use app\data\service\payment\WechatPaymentService;
  12. use think\admin\Exception;
  13. use think\App;
  14. /**
  15. * 支付基础服务
  16. * Class PaymentService
  17. * @package app\data\service
  18. */
  19. abstract class PaymentService
  20. {
  21. // 用户余额支付
  22. const PAYMENT_EMPTY = 'empty';
  23. const PAYMENT_BALANCE = 'balance';
  24. const PAYMENT_VOUCHER = 'voucher';
  25. // 汇聚支付参数
  26. const PAYMENT_JOINPAY_GZH = 'joinpay_gzh';
  27. const PAYMENT_JOINPAY_XCX = 'joinpay_xcx';
  28. // 微信商户支付
  29. const PAYMENT_WECHAT_APP = 'wechat_app';
  30. const PAYMENT_WECHAT_GZH = 'wechat_gzh';
  31. const PAYMENT_WECHAT_XCX = 'wechat_xcx';
  32. const PAYMENT_WECHAT_WAP = 'wechat_wap';
  33. const PAYMENT_WECHAT_QRC = 'wechat_qrc';
  34. // 支付宝支付参数
  35. const PAYMENT_ALIAPY_APP = 'alipay_app';
  36. const PAYMENT_ALIPAY_WAP = 'alipay_wap';
  37. const PAYMENT_ALIPAY_WEB = 'alipay_web';
  38. // 支付参数配置
  39. const TYPES = [
  40. // 微信支付配置(不需要的直接注释)
  41. self::PAYMENT_EMPTY => [
  42. 'type' => 'EMPTY',
  43. 'name' => '订单无需支付',
  44. 'bind' => [],
  45. ],
  46. self::PAYMENT_BALANCE => [
  47. 'type' => 'BALANCE',
  48. 'name' => '账号余额支付',
  49. 'bind' => [
  50. UserAdminService::API_TYPE_WAP, UserAdminService::API_TYPE_WEB,
  51. UserAdminService::API_TYPE_WXAPP, UserAdminService::API_TYPE_WECHAT,
  52. UserAdminService::API_TYPE_IOSAPP, UserAdminService::API_TYPE_ANDROID,
  53. ],
  54. ],
  55. self::PAYMENT_VOUCHER => [
  56. 'type' => 'VOUCHER',
  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. self::PAYMENT_WECHAT_WAP => [
  65. 'type' => 'MWEB',
  66. 'name' => '微信WAP支付',
  67. 'bind' => [UserAdminService::API_TYPE_WAP],
  68. ],
  69. self::PAYMENT_WECHAT_APP => [
  70. 'type' => 'APP',
  71. 'name' => '微信APP支付',
  72. 'bind' => [UserAdminService::API_TYPE_IOSAPP, UserAdminService::API_TYPE_ANDROID],
  73. ],
  74. self::PAYMENT_WECHAT_XCX => [
  75. 'type' => 'JSAPI',
  76. 'name' => '微信小程序支付',
  77. 'bind' => [UserAdminService::API_TYPE_WXAPP],
  78. ],
  79. self::PAYMENT_WECHAT_GZH => [
  80. 'type' => 'JSAPI',
  81. 'name' => '微信公众号支付',
  82. 'bind' => [UserAdminService::API_TYPE_WECHAT],
  83. ],
  84. self::PAYMENT_WECHAT_QRC => [
  85. 'type' => 'NATIVE',
  86. 'name' => '微信二维码支付',
  87. 'bind' => [UserAdminService::API_TYPE_WEB],
  88. ],
  89. // 支付宝支持配置(不需要的直接注释)
  90. self::PAYMENT_ALIPAY_WAP => [
  91. 'type' => '',
  92. 'name' => '支付宝WAP支付',
  93. 'bind' => [UserAdminService::API_TYPE_WAP],
  94. ],
  95. self::PAYMENT_ALIPAY_WEB => [
  96. 'type' => '',
  97. 'name' => '支付宝WEB支付',
  98. 'bind' => [UserAdminService::API_TYPE_WEB],
  99. ],
  100. self::PAYMENT_ALIAPY_APP => [
  101. 'type' => '',
  102. 'name' => '支付宝APP支付',
  103. 'bind' => [UserAdminService::API_TYPE_ANDROID, UserAdminService::API_TYPE_IOSAPP],
  104. ],
  105. // 汇聚支持配置(不需要的直接注释)
  106. self::PAYMENT_JOINPAY_XCX => [
  107. 'type' => 'WEIXIN_XCX',
  108. 'name' => '汇聚小程序支付',
  109. 'bind' => [UserAdminService::API_TYPE_WXAPP],
  110. ],
  111. self::PAYMENT_JOINPAY_GZH => [
  112. 'type' => 'WEIXIN_GZH',
  113. 'name' => '汇聚公众号支付',
  114. 'bind' => [UserAdminService::API_TYPE_WECHAT],
  115. ],
  116. ];
  117. /**
  118. * 支付服务对象
  119. * @var array
  120. */
  121. protected static $driver = [];
  122. /**
  123. * 当前应用
  124. * @var App
  125. */
  126. protected $app;
  127. /**
  128. * 支付参数编号
  129. * @var string
  130. */
  131. protected $code;
  132. /**
  133. * 默认支付类型
  134. * @var string
  135. */
  136. protected $type;
  137. /**
  138. * 当前支付参数
  139. * @var array
  140. */
  141. protected $params;
  142. /**
  143. * PaymentService constructor.
  144. * @param App $app 当前应用对象
  145. * @param string $code 支付参数编号
  146. * @param string $type 支付类型代码
  147. * @param array $params 支付参数配置
  148. */
  149. public function __construct(App $app, string $code, string $type, array $params)
  150. {
  151. [$this->app, $this->code, $this->type, $this->params] = [$app, $code, $type, $params];
  152. if (method_exists($this, 'initialize')) $this->initialize();
  153. }
  154. /**
  155. * 根据配置实例支付服务
  156. * @param string $code 支付配置编号
  157. * @return JoinpayPaymentService|WechatPaymentService|AlipayPaymentService|BalancePyamentService|VoucherPaymentService|EmptyPaymentService
  158. * @throws \think\admin\Exception
  159. */
  160. public static function instance(string $code): PaymentService
  161. {
  162. if ($code === 'empty') {
  163. $vars = ['code' => 'empty', 'type' => 'empty', 'params' => []];
  164. return static::$driver[$code] = app()->make(EmptyPaymentService::class, $vars);
  165. }
  166. [, $type, $params] = self::config($code);
  167. if (isset(static::$driver[$code])) return static::$driver[$code];
  168. $vars = ['code' => $code, 'type' => $type, 'params' => $params];
  169. // 实例化具体支付参数类型
  170. if (stripos($type, 'balance') === 0) {
  171. return static::$driver[$code] = app()->make(BalancePyamentService::class, $vars);
  172. } elseif (stripos($type, 'voucher') === 0) {
  173. return static::$driver[$code] = app()->make(VoucherPaymentService::class, $vars);
  174. } elseif (stripos($type, 'alipay_') === 0) {
  175. return static::$driver[$code] = app()->make(AlipayPaymentService::class, $vars);
  176. } elseif (stripos($type, 'wechat_') === 0) {
  177. return static::$driver[$code] = app()->make(WechatPaymentService::class, $vars);
  178. } elseif (stripos($type, 'joinpay_') === 0) {
  179. return static::$driver[$code] = app()->make(JoinpayPaymentService::class, $vars);
  180. } else {
  181. throw new Exception(sprintf('支付驱动[%s]未定义', $type));
  182. }
  183. }
  184. /**
  185. * 获取支付配置参数
  186. * @param string $code
  187. * @param array $payment
  188. * @return array [code, type, params]
  189. * @throws Exception
  190. */
  191. public static function config(string $code, array $payment = []): array
  192. {
  193. try {
  194. if (empty($payment)) {
  195. $map = ['code' => $code, 'status' => 1, 'deleted' => 0];
  196. $payment = BaseUserPayment::mk()->where($map)->find();
  197. }
  198. if (empty($payment)) {
  199. throw new Exception("支付参数[#{$code}]禁用关闭");
  200. }
  201. $params = @json_decode($payment['content'], true);
  202. if (empty($params)) {
  203. throw new Exception("支付参数[#{$code}]配置无效");
  204. }
  205. if (empty(static::TYPES[$payment['type']])) {
  206. throw new Exception("支付参数[@{$payment['type']}]匹配失败");
  207. }
  208. return [$payment['code'], $payment['type'], $params];
  209. } catch (\Exception $exception) {
  210. throw new Exception($exception->getMessage(), $exception->getCode());
  211. }
  212. }
  213. /**
  214. * 获取支付支付名称
  215. * @param string $type
  216. * @return string
  217. */
  218. public static function name(string $type): string
  219. {
  220. return self::TYPES[$type]['name'] ?? $type;
  221. }
  222. /**
  223. * 获取支付类型
  224. * @param array $types 默认返回支付
  225. * @return array
  226. */
  227. public static function getTypeAll(array $types = []): array
  228. {
  229. $binds = array_keys(UserAdminService::TYPES);
  230. foreach (self::TYPES as $k => $v) if (isset($v['bind'])) {
  231. if (array_intersect($v['bind'], $binds)) $types[$k] = $v;
  232. }
  233. return $types;
  234. }
  235. /**
  236. * 筛选可用的支付类型
  237. * @param string $api 指定接口类型
  238. * @param array $types 默认返回支付
  239. * @return array
  240. */
  241. public static function getTypeApi(string $api = '', array $types = []): array
  242. {
  243. foreach (self::TYPES as $type => $attr) {
  244. if (in_array($api, $attr['bind'])) $types[] = $type;
  245. }
  246. return array_unique($types);
  247. }
  248. /**
  249. * 订单主动查询
  250. * @param string $orderNo
  251. * @return array
  252. */
  253. abstract public function query(string $orderNo): array;
  254. /**
  255. * 支付通知处理
  256. * @return string
  257. */
  258. abstract public function notify(): string;
  259. /**
  260. * 创建支付订单
  261. * @param string $openid 用户OPENID
  262. * @param string $orderNo 交易订单单号
  263. * @param string $paymentAmount 交易订单金额(元)
  264. * @param string $paymentTitle 交易订单名称
  265. * @param string $paymentRemark 交易订单描述
  266. * @param string $paymentReturn 支付回跳地址
  267. * @param string $paymentImage 支付凭证图片
  268. * @return array
  269. */
  270. abstract public function create(string $openid, string $orderNo, string $paymentAmount, string $paymentTitle, string $paymentRemark, string $paymentReturn = '', string $paymentImage = ''): array;
  271. /**
  272. * 创建支付行为
  273. * @param string $orderNo 商户订单单号
  274. * @param string $paymentTitle 商户订单标题
  275. * @param string $paymentAmount 需要支付金额
  276. */
  277. protected function createPaymentAction(string $orderNo, string $paymentTitle, string $paymentAmount)
  278. {
  279. DataUserPayment::mk()->insert([
  280. 'payment_code' => $this->code,
  281. 'payment_type' => $this->type,
  282. 'order_no' => $orderNo,
  283. 'order_name' => $paymentTitle,
  284. 'order_amount' => $paymentAmount,
  285. ]);
  286. }
  287. /**
  288. * 更新支付记录并更新订单
  289. * @param string $orderNo 商户订单单号
  290. * @param string $paymentTrade 平台交易单号
  291. * @param string $paymentAmount 实际到账金额
  292. * @param string $paymentRemark 平台支付备注
  293. * @return boolean
  294. * @throws \think\db\exception\DataNotFoundException
  295. * @throws \think\db\exception\DbException
  296. * @throws \think\db\exception\ModelNotFoundException
  297. */
  298. protected function updatePaymentAction(string $orderNo, string $paymentTrade, string $paymentAmount, string $paymentRemark = '在线支付'): bool
  299. {
  300. // 更新支付记录
  301. data_save(DataUserPayment::mk(), [
  302. 'order_no' => $orderNo,
  303. 'payment_code' => $this->code,
  304. 'payment_type' => $this->type,
  305. 'payment_trade' => $paymentTrade,
  306. 'payment_amount' => $paymentAmount,
  307. 'payment_status' => 1,
  308. 'payment_datatime' => date('Y-m-d H:i:s'),
  309. ], 'order_no', [
  310. 'payment_code' => $this->code,
  311. 'payment_type' => $this->type,
  312. ]);
  313. // 更新记录状态
  314. return $this->updateOrder($orderNo, $paymentTrade, $paymentAmount, $paymentRemark);
  315. }
  316. /**
  317. * 订单支付更新操作
  318. * @param string $orderNo 订单单号
  319. * @param string $paymentTrade 交易单号
  320. * @param string $paymentAmount 支付金额
  321. * @param string $paymentRemark 支付描述
  322. * @param string $paymentImage 支付凭证
  323. * @return boolean
  324. * @throws \think\db\exception\DataNotFoundException
  325. * @throws \think\db\exception\DbException
  326. * @throws \think\db\exception\ModelNotFoundException
  327. */
  328. public function updateOrder(string $orderNo, string $paymentTrade, string $paymentAmount, string $paymentRemark = '在线支付', string $paymentImage = ''): bool
  329. {
  330. $map = ['status' => 2, 'order_no' => $orderNo, 'payment_status' => 0];
  331. $order = ShopOrder::mk()->where($map)->find();
  332. if (empty($order)) return false;
  333. // 检查订单支付状态
  334. if ($this->type === self::PAYMENT_VOUCHER) {
  335. $status = 3; # 凭证支付需要审核
  336. } elseif (empty($order['truck_type'])) {
  337. $status = 6; # 虚拟订单直接完成
  338. } else {
  339. $status = 4; # 实物订单需要发货
  340. }
  341. // 更新订单支付状态
  342. $data = [
  343. 'status' => $status,
  344. 'payment_type' => $this->type,
  345. 'payment_code' => $this->code,
  346. 'payment_trade' => $paymentTrade,
  347. 'payment_image' => $paymentImage,
  348. 'payment_amount' => $paymentAmount,
  349. 'payment_remark' => $paymentRemark,
  350. 'payment_status' => 1,
  351. 'payment_datetime' => date('Y-m-d H:i:s'),
  352. ];
  353. if (empty($data['payment_type'])) unset($data['payment_type']);
  354. ShopOrder::mk()->where($map)->update($data);
  355. // 触发订单更新事件
  356. if ($status >= 4) {
  357. $this->app->event->trigger('ShopOrderPayment', $orderNo);
  358. }
  359. return true;
  360. }
  361. }