Douyin.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. namespace app\common\Service\Pay\Provider;
  3. use think\Log;
  4. use think\Exception;
  5. use think\exception\HttpResponseException;
  6. use Yansongda\Pay\Pay;
  7. use app\common\Enum\ChannelEnum;
  8. /**
  9. * 抖音支付提供器
  10. */
  11. class Douyin extends Base
  12. {
  13. /**
  14. * 构造函数
  15. * @param mixed $payService
  16. * @param string $platform
  17. * @param string $channel
  18. */
  19. public function __construct($payService, $platform = null, $channel = null)
  20. {
  21. parent::__construct($payService, $platform, $channel);
  22. }
  23. /**
  24. * 抖音支付
  25. * @param array $order
  26. * @param array $config
  27. * @return array
  28. */
  29. public function pay($order, $config = [])
  30. {
  31. $this->init('douyin', $config);
  32. // 按照抖音支付API要求构建订单参数
  33. $orderData = [
  34. 'out_order_no' => $order['out_trade_no'],
  35. 'total_amount' => intval($order['total_amount']), // 抖音支付金额单位为分
  36. 'subject' => $order['subject'] ?? '商品支付',
  37. 'body' => $order['body'] ?? '商品支付',
  38. 'valid_time' => $config['valid_time'] ?? 600, // 默认10分钟
  39. ];
  40. // 添加可选参数
  41. if (isset($order['cp_extra'])) {
  42. $orderData['cp_extra'] = $order['cp_extra'];
  43. }
  44. if (isset($config['disable_msg'])) {
  45. $orderData['disable_msg'] = $config['disable_msg'];
  46. }
  47. if (isset($config['msg_page'])) {
  48. $orderData['msg_page'] = $config['msg_page'];
  49. }
  50. // 移除不需要的字段
  51. unset($order['order_id'], $order['total_amount']);
  52. // 获取支付方法
  53. $method = $this->getMethod('douyin');
  54. // 调用 Yansongda\Pay 进行支付
  55. $result = Pay::douyin()->$method($orderData);
  56. Log::write('抖音支付预下单响应:' . json_encode($result->toArray(), JSON_UNESCAPED_UNICODE));
  57. return $result;
  58. }
  59. /**
  60. * 转账功能(抖音支付暂不支持)
  61. * @param array $payload
  62. * @param array $config
  63. * @return array
  64. */
  65. public function transfer($payload, $config = [])
  66. {
  67. throw new Exception('抖音支付暂不支持转账功能');
  68. }
  69. /**
  70. * 支付回调处理
  71. * @param callable $callback
  72. * @param array $config
  73. * @return string
  74. */
  75. public function notify($callback, $config = [])
  76. {
  77. $this->init('douyin', $config);
  78. try {
  79. // 使用 Yansongda\Pay 处理回调验签
  80. $data = Pay::douyin()->callback();
  81. Log::write('抖音支付回调原始数据:' . json_encode($data, JSON_UNESCAPED_UNICODE));
  82. // 处理支付成功回调
  83. if ($data['type'] === 'payment' && $data['msg'] === 'success') {
  84. // 格式化数据为统一格式
  85. $callbackData = [
  86. 'out_trade_no' => $data['out_order_no'],
  87. 'transaction_id' => $data['order_id'],
  88. 'total_amount' => $data['total_amount'],
  89. 'pay_fee' => $data['total_amount'], // 抖音支付金额单位为分,这里保持原样
  90. 'notify_time' => date('Y-m-d H:i:s'),
  91. 'buyer_info' => $data['buyer_info'] ?? '',
  92. 'cp_extra' => $data['cp_extra'] ?? ''
  93. ];
  94. $result = $callback($callbackData, $data);
  95. return $result;
  96. }
  97. Log::error('抖音支付回调类型不支持: ' . ($data['type'] ?? 'unknown'));
  98. return 'fail';
  99. } catch (HttpResponseException $e) {
  100. $data = $e->getResponse()->getData();
  101. $message = $data ? ($data['msg'] ?? '') : $e->getMessage();
  102. $this->logPayment('抖音支付回调HttpResponseException', ['error' => $message], 'error');
  103. return 'fail';
  104. } catch (\Exception $e) {
  105. $this->logPayment('抖音支付回调异常', ['error' => $e->getMessage()], 'error');
  106. return 'fail';
  107. }
  108. }
  109. /**
  110. * 退款
  111. * @param array $order_data
  112. * @param array $config
  113. * @return array
  114. */
  115. public function refund($order_data, $config = [])
  116. {
  117. $this->init('douyin', $config);
  118. // 构建退款参数
  119. $refundData = [
  120. 'out_order_no' => $order_data['out_trade_no'],
  121. 'out_refund_no' => $order_data['out_refund_no'],
  122. 'refund_amount' => intval($order_data['refund_amount']), // 抖音退款金额单位为分
  123. 'reason' => $order_data['reason'] ?? '用户申请退款',
  124. ];
  125. // 如果有回调地址配置
  126. if (isset($config['notify_url'])) {
  127. $refundData['notify_url'] = $config['notify_url'];
  128. }
  129. // 调用 Yansongda\Pay 进行退款
  130. $result = Pay::douyin()->refund($refundData);
  131. Log::write('抖音退款响应:' . json_encode($result->toArray(), JSON_UNESCAPED_UNICODE));
  132. return $result;
  133. }
  134. /**
  135. * 退款回调处理
  136. * @param callable $callback
  137. * @param array $config
  138. * @return string
  139. */
  140. public function notifyRefund($callback, $config = [])
  141. {
  142. $this->init('douyin', $config);
  143. try {
  144. // 使用 Yansongda\Pay 处理退款回调验签
  145. $data = Pay::douyin()->callback();
  146. Log::write('抖音退款回调原始数据:' . json_encode($data, JSON_UNESCAPED_UNICODE));
  147. // 处理退款成功回调
  148. if ($data['type'] === 'refund' && $data['msg'] === 'success') {
  149. $result = $callback($data, $data);
  150. return $result;
  151. }
  152. Log::error('抖音退款回调类型不支持: ' . ($data['type'] ?? 'unknown'));
  153. return 'fail';
  154. } catch (HttpResponseException $e) {
  155. $data = $e->getResponse()->getData();
  156. $message = $data ? ($data['msg'] ?? '') : $e->getMessage();
  157. $this->logPayment('抖音退款回调HttpResponseException', ['error' => $message], 'error');
  158. return 'fail';
  159. } catch (\Exception $e) {
  160. $this->logPayment('抖音退款回调异常', ['error' => $e->getMessage()], 'error');
  161. return 'fail';
  162. }
  163. }
  164. /**
  165. * 格式化支付参数
  166. * @param array $config
  167. * @param array $data
  168. * @param string $type
  169. * @return array
  170. */
  171. protected function formatConfig($config, $data = [], $type = 'normal')
  172. {
  173. // 设置抖音支付的基本配置
  174. $config['app_id'] = $data['app_id'] ?? '';
  175. $config['secret'] = $data['secret'] ?? $data['salt'] ?? '';
  176. // 设置回调URL
  177. $platform = $this->platform ?? 'DouyinMiniProgram';
  178. $config['notify_url'] = request()->domain() . '/api/pay/notify/payment/douyin/platform/' . $platform;
  179. // 设置抖音支付网关(沙盒和生产环境)
  180. if (isset($config['sandbox']) && $config['sandbox']) {
  181. $config['mode'] = Pay::MODE_SANDBOX;
  182. } else {
  183. $config['mode'] = Pay::MODE_NORMAL;
  184. }
  185. return $config;
  186. }
  187. }