PayPlusLogic.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <?php
  2. namespace App\Http\logic\api;
  3. use App\dao\Pay\AccountPayInfo;
  4. use App\dao\Pay\PayController;
  5. use App\Http\helper\CreateOrder;
  6. use App\Http\helper\NumConfig;
  7. use App\Jobs\Order;
  8. use App\Notification\TelegramBot;
  9. use App\Services\CreateLog;
  10. use App\Services\OrderServices;
  11. use App\Services\PayConfig;
  12. use App\Services\PayPlus;
  13. use App\Util;
  14. use Illuminate\Support\Facades\DB;
  15. class PayPlusLogic extends BaseApiLogic
  16. {
  17. protected $service;
  18. public function __construct(PayPlus $service = null)
  19. {
  20. $this->service = $service ?: new PayPlus();
  21. }
  22. public function pay_order(
  23. $userId,
  24. $payAmount,
  25. $userPhone,
  26. $userEmail,
  27. $userName,
  28. $GiftsID,
  29. $buyIP,
  30. $AdId,
  31. $eventType,
  32. $payMethod = ''
  33. ) {
  34. $dao = new AccountPayInfo();
  35. list($userPhone, $userName, $userEmail) = $dao->payInfo($userId);
  36. $payVerify = new PayController();
  37. $payAmount = $payVerify->verify($userId, $GiftsID, $payAmount);
  38. if ($payAmount === false || $payAmount < 0) {
  39. $this->error = $payVerify->getError() ?: 'Payment error_4';
  40. return false;
  41. }
  42. $orderSn = CreateOrder::order_sn($userId);
  43. $amount = (int) round($payAmount * NumConfig::NUM_VALUE);
  44. $logic = new OrderLogic();
  45. if (
  46. !$logic->orderCreate(
  47. $orderSn,
  48. $amount,
  49. 'PayPlus',
  50. $userId,
  51. $payMethod,
  52. $GiftsID,
  53. $AdId,
  54. $eventType
  55. )
  56. ) {
  57. $this->error = $logic->getError();
  58. return false;
  59. }
  60. $payload = $this->buildPaymentPayload([
  61. 'order_sn' => $orderSn,
  62. 'amount' => $payAmount,
  63. 'user_id' => $userId,
  64. 'user_email' => $userEmail,
  65. 'user_phone' => $userPhone,
  66. 'user_name' => $userName,
  67. 'buy_ip' => $buyIP,
  68. 'pay_method' => $payMethod,
  69. ]);
  70. CreateLog::pay_request(
  71. $userPhone,
  72. json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
  73. $orderSn,
  74. $userEmail,
  75. $userId,
  76. $userName
  77. );
  78. Util::WriteLog('PayPlus', 'PayPlus payment request: ' . json_encode($payload));
  79. try {
  80. $result = $this->service->postPayin($payload);
  81. Util::WriteLog('PayPlus', 'PayPlus payment response: ' . json_encode($result));
  82. if ($result['code'] !== 200) {
  83. TelegramBot::getDefault()->sendProgramNotify(
  84. 'PayPlus payment failed',
  85. 'Response: ' . json_encode($result),
  86. null
  87. );
  88. }
  89. return $result;
  90. } catch (\Exception $exception) {
  91. Util::WriteLog('PayPlus_error', $exception->getMessage());
  92. $this->error = 'Payment processing error';
  93. return false;
  94. }
  95. }
  96. public function buildPaymentPayload(array $input)
  97. {
  98. $config = $this->service->getConfig();
  99. $payMethod = (int) ($input['pay_method'] ?: 1);
  100. $methodMap = $config['payment_methods'] ?? [
  101. 1 => 8,
  102. 2 => 2,
  103. 4 => 1,
  104. 8 => 5,
  105. ];
  106. $nameParts = preg_split('/\s+/', trim((string) ($input['user_name'] ?? '')), 2);
  107. return [
  108. 'order_type' => 'RECHARGE',
  109. 'platform_order_id' => (string) $input['order_sn'],
  110. 'currency' => strtoupper($config['currency'] ?? 'USD'),
  111. 'amount' => number_format((float) $input['amount'], 2, '.', ''),
  112. 'payment_method' => $methodMap[$payMethod] ?? 8,
  113. 'return_url' => $config['return'] ?? '',
  114. 'cancel_url' => $config['cancel'] ?? ($config['return'] ?? ''),
  115. 'connection_info' => [
  116. 'ip' => $input['buy_ip'] ?: '0.0.0.0',
  117. 'country' => $config['country'] ?? 'US',
  118. 'state' => $config['state'] ?? 'NA',
  119. 'zip_code' => $config['zip'] ?? '00000',
  120. 'media_source' => $config['media_source'] ?? 'organic',
  121. 'language' => $config['language'] ?? 'en-US',
  122. ],
  123. 'account_info' => [
  124. 'merchant_user_id' => (string) $input['user_id'],
  125. 'create_time' => time()*1000,
  126. 'role' => 'PRIVATE',
  127. 'email' => $this->emailOrDefault($input['user_email'] ?? '', $input['user_id']),
  128. 'phone' => preg_replace('/\D+/', '', (string) ($input['user_phone'] ?? '')) ?: '0000000000',
  129. 'area_code' => $config['area_code'] ?? '1',
  130. 'first_name' => 'user',
  131. 'last_name' => 'user',
  132. 'vip_level' => 0,
  133. ],
  134. ];
  135. }
  136. public function notify(array $post)
  137. {
  138. $orderSn = $post['data']['platform_order_id'] ?? '';
  139. if ($orderSn === '') {
  140. return 'success';
  141. }
  142. $order = DB::connection('write')
  143. ->table('agent.dbo.order')
  144. ->where('order_sn', $orderSn)
  145. ->first();
  146. if (!$order || !empty($order->pay_at) || !empty($order->finished_at)) {
  147. return 'success';
  148. }
  149. $data = $post['data'] ?? [];
  150. $body = [
  151. 'payment_sn' => $data['order_id'] ?? '',
  152. 'updated_at' => date('Y-m-d H:i:s'),
  153. ];
  154. if ($this->isSuccessfulPayment($post)) {
  155. $AdId = $order->AdId ?: '';
  156. $eventType = $order->eventType ?: '';
  157. $payAmount = round((float) ($data['amount'] ?? 0), 2);
  158. $body['pay_status'] = 1;
  159. $body['pay_at'] = date('Y-m-d H:i:s');
  160. $body['finished_at'] = date('Y-m-d H:i:s');
  161. $body['amount'] = (int) round($payAmount * NumConfig::NUM_VALUE);
  162. $config = (new PayConfig())->getConfig('PayPlus');
  163. // 根据支付方式计算代收手续费: 费率% * 金额 + 固定$
  164. // pay_rate 格式: [1=>[10,0.3], 2=>[13,0.3], 4=>[11,0.3], 8=>[12,0.3]]
  165. $payRates = $config['pay_rate'] ?? null;
  166. if (is_array($payRates)) {
  167. $payMethod = $order->order_title ?? 1;
  168. $payRate = $payRates[$payMethod] ?? ($payRates[1] ?? [10, 0.3]);
  169. $feePercent = $payRate[0] ?? 10;
  170. $feeFixed = $payRate[1] ?? 0.3;
  171. $body['payment_fee'] = intval(($body['amount'] * $feePercent) / 100)
  172. + (int)($feeFixed * NumConfig::NUM_VALUE);
  173. }
  174. $service = new OrderServices();
  175. list($give, $favorablePrice, $recharge, $czReason, $cjReason) = $service->getPayInfo(
  176. $order->GiftsID ?: '',
  177. $order->user_id ?: '',
  178. $payAmount
  179. );
  180. list($score) = $service->addRecord(
  181. $order->user_id,
  182. $payAmount,
  183. $favorablePrice,
  184. $orderSn,
  185. $order->GiftsID,
  186. $recharge,
  187. $czReason,
  188. $give,
  189. $cjReason,
  190. $order->AdId ?: '',
  191. $order->eventType ?: '',
  192. $body['payment_fee'] ?? 0
  193. );
  194. Order::dispatch([
  195. $order->user_id,
  196. $payAmount,
  197. $score,
  198. $favorablePrice,
  199. $order->GiftsID,
  200. $orderSn,
  201. ]);
  202. } elseif ($this->isFailedPayment($post)) {
  203. $body['pay_status'] = 2;
  204. } else {
  205. return 'success';
  206. }
  207. DB::connection('write')
  208. ->table('agent.dbo.order')
  209. ->where('order_sn', $orderSn)
  210. ->update($body);
  211. return 'success';
  212. }
  213. public function isSuccessfulPayment(array $post)
  214. {
  215. return ($post['event'] ?? '') === 'PAYMENT.CAPTURE.COMPLETED'
  216. && ($post['event_detail_name'] ?? '') === 'payment_captured'
  217. && (int) ($post['data']['order_status'] ?? 0) === 3;
  218. }
  219. public function isFailedPayment(array $post)
  220. {
  221. $event = $post['event'] ?? '';
  222. $detail = $post['event_detail_name'] ?? '';
  223. $status = (int) ($post['data']['order_status'] ?? 0);
  224. return ($event === 'PAYMENT.CAPTURE.COMPLETED' && in_array($status, [4, 10, 11], true))
  225. || $event === 'PAYMENT.ORDER.TIMEOUT'
  226. || in_array($detail, ['payment_declined', 'payment_timeout'], true);
  227. }
  228. protected function resolvePaymentUrl(array $result)
  229. {
  230. $data = $result['data'] ?? $result;
  231. foreach (['cashierUrl', 'cashier_url', 'payment_url', 'paymentUrl', 'redirect_url', 'url'] as $key) {
  232. if (!empty($data[$key])) {
  233. return $data[$key];
  234. }
  235. }
  236. return '';
  237. }
  238. protected function emailOrDefault($email, $userId)
  239. {
  240. return filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : 'unknown' . $userId . '@example.com';
  241. }
  242. protected function stringOrDefault($value, $default)
  243. {
  244. $value = trim((string) $value);
  245. return $value === '' ? $default : $value;
  246. }
  247. }