PaymentService.php 14 KB

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