Douyin.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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_order_no'] ?? $order['out_trade_no'],
  35. 'total_amount' => intval(bcmul((string)$order['total_amount'], '100')), // 抖音支付金额单位为分
  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. $method = $this->getMethod('douyin');
  52. // 调用 Yansongda\Pay 进行支付
  53. $result = Pay::douyin()->$method($orderData);
  54. Log::write('抖音支付预下单响应:' . json_encode($result->toArray(), JSON_UNESCAPED_UNICODE));
  55. return $result;
  56. }
  57. /**
  58. * 转账功能(抖音支付暂不支持)
  59. * @param array $payload
  60. * @param array $config
  61. * @return array
  62. */
  63. public function transfer($payload, $config = [])
  64. {
  65. throw new Exception('抖音支付暂不支持转账功能');
  66. }
  67. /**
  68. * 支付回调处理
  69. * @param callable $callback
  70. * @param array $config
  71. * @return string
  72. */
  73. public function notify($callback, $config = [])
  74. {
  75. $this->init('douyin', $config);
  76. try {
  77. // 使用 Yansongda\Pay 处理回调验签
  78. $data = Pay::douyin()->callback();
  79. Log::write('抖音支付回调原始数据:' . json_encode($data, JSON_UNESCAPED_UNICODE));
  80. // 处理支付成功回调
  81. if ($data['type'] === 'payment') {
  82. // 格式化数据为统一格式
  83. $arrMsg = json_decode($data['msg'], true);
  84. $callbackData = [
  85. 'out_trade_no' => $arrMsg['cp_orderno'] ?? '',
  86. 'transaction_id' => $arrMsg['order_id'] ?? '',
  87. 'total_amount' => $arrMsg['total_amount'] ?? 0,
  88. 'pay_fee' => $arrMsg['total_amount'] ?? 0, // 抖音支付金额单位为分,这里保持原样
  89. 'notify_time' => date('Y-m-d H:i:s'),
  90. // 'buyer_info' => $data['buyer_info'] ?? '',
  91. // 'cp_extra' => $data['cp_extra'] ?? ''
  92. ];
  93. $result = $callback($callbackData, $data);
  94. return $result;
  95. }
  96. Log::error('抖音支付回调类型不支持: ' . ($data['type'] ?? 'unknown'));
  97. return 'fail';
  98. } catch (HttpResponseException $e) {
  99. $data = $e->getResponse()->getData();
  100. $message = $data ? ($data['msg'] ?? '') : $e->getMessage();
  101. $this->logPayment('抖音支付回调HttpResponseException', ['error' => $message], 'error');
  102. return 'fail';
  103. } catch (\Exception $e) {
  104. $this->logPayment('抖音支付回调异常', ['error' => $e->getMessage()], 'error');
  105. return 'fail';
  106. }
  107. }
  108. /**
  109. * 退款
  110. * @param array $order_data
  111. * @param array $config
  112. * @return array
  113. */
  114. public function refund($order_data, $config = [])
  115. {
  116. $this->init('douyin', $config);
  117. // 构建退款参数
  118. $refundData = [
  119. 'out_order_no' => $order_data['out_trade_no'],
  120. 'out_refund_no' => $order_data['out_refund_no'],
  121. 'refund_amount' => intval($order_data['refund_amount']), // 抖音退款金额单位为分
  122. 'reason' => $order_data['reason'] ?? '用户申请退款',
  123. ];
  124. // 如果有回调地址配置
  125. if (isset($config['notify_url'])) {
  126. $refundData['notify_url'] = $config['notify_url'];
  127. }
  128. // 调用 Yansongda\Pay 进行退款
  129. $result = Pay::douyin()->refund($refundData);
  130. Log::write('抖音退款响应:' . json_encode($result->toArray(), JSON_UNESCAPED_UNICODE));
  131. return $result;
  132. }
  133. /**
  134. * 退款回调处理
  135. * @param callable $callback
  136. * @param array $config
  137. * @return string
  138. */
  139. public function notifyRefund($callback, $config = [])
  140. {
  141. $this->init('douyin', $config);
  142. try {
  143. // 使用 Yansongda\Pay 处理退款回调验签
  144. $data = Pay::douyin()->callback();
  145. Log::write('抖音退款回调原始数据:' . json_encode($data, JSON_UNESCAPED_UNICODE));
  146. // 处理退款成功回调
  147. if ($data['type'] === 'refund' && $data['msg'] === 'success') {
  148. $result = $callback($data, $data);
  149. return $result;
  150. }
  151. Log::error('抖音退款回调类型不支持: ' . ($data['type'] ?? 'unknown'));
  152. return 'fail';
  153. } catch (HttpResponseException $e) {
  154. $data = $e->getResponse()->getData();
  155. $message = $data ? ($data['msg'] ?? '') : $e->getMessage();
  156. $this->logPayment('抖音退款回调HttpResponseException', ['error' => $message], 'error');
  157. return 'fail';
  158. } catch (\Exception $e) {
  159. $this->logPayment('抖音退款回调异常', ['error' => $e->getMessage()], 'error');
  160. return 'fail';
  161. }
  162. }
  163. /**
  164. * 格式化支付参数
  165. * @param array $config
  166. * @param array $data
  167. * @param string $type
  168. * @return array
  169. */
  170. protected function formatConfig($config, $data = [], $type = 'normal')
  171. {
  172. // 设置抖音支付的基本配置
  173. $config['app_id'] = $data['app_id'] ?? '';
  174. $config['secret'] = $data['secret'] ?? $data['salt'] ?? '';
  175. // 设置回调URL
  176. $platform = $this->platform ?? 'DouyinMiniProgram';
  177. $config['notify_url'] = request()->domain() . '/api/pay/notify/payment/douyin/platform/' . $platform;
  178. // 设置抖音支付网关(沙盒和生产环境)
  179. if (isset($config['sandbox']) && $config['sandbox']) {
  180. $config['mode'] = Pay::MODE_SANDBOX;
  181. } else {
  182. $config['mode'] = Pay::MODE_NORMAL;
  183. }
  184. return $config;
  185. }
  186. }