service = $service ?: new PayPlus(); } public function pay_order( $userId, $payAmount, $userPhone, $userEmail, $userName, $GiftsID, $buyIP, $AdId, $eventType, $payMethod = '' ) { $dao = new AccountPayInfo(); list($userPhone, $userName, $userEmail) = $dao->payInfo($userId); $payVerify = new PayController(); $payAmount = $payVerify->verify($userId, $GiftsID, $payAmount); if ($payAmount === false || $payAmount < 0) { $this->error = $payVerify->getError() ?: 'Payment error_4'; return false; } $orderSn = CreateOrder::order_sn($userId); $amount = (int) round($payAmount * NumConfig::NUM_VALUE); $logic = new OrderLogic(); if ( !$logic->orderCreate( $orderSn, $amount, 'PayPlus', $userId, $payMethod, $GiftsID, $AdId, $eventType ) ) { $this->error = $logic->getError(); return false; } $payload = $this->buildPaymentPayload([ 'order_sn' => $orderSn, 'amount' => $payAmount, 'user_id' => $userId, 'user_email' => $userEmail, 'user_phone' => $userPhone, 'user_name' => $userName, 'buy_ip' => $buyIP, 'pay_method' => $payMethod, ]); CreateLog::pay_request( $userPhone, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), $orderSn, $userEmail, $userId, $userName ); Util::WriteLog('PayPlus', 'PayPlus payment request: ' . json_encode($payload)); try { $result = $this->service->postPayin($payload); Util::WriteLog('PayPlus', 'PayPlus payment response: ' . json_encode($result)); if ($result['code'] !== 200) { TelegramBot::getDefault()->sendProgramNotify( 'PayPlus payment failed', 'Response: ' . json_encode($result), null ); } return $result; } catch (\Exception $exception) { Util::WriteLog('PayPlus_error', $exception->getMessage()); $this->error = 'Payment processing error'; return false; } } public function buildPaymentPayload(array $input) { $config = $this->service->getConfig(); $payMethod = (int) ($input['pay_method'] ?: 1); $methodMap = $config['payment_methods'] ?? [ 1 => 8, 2 => 2, 4 => 1, 8 => 5, ]; $nameParts = preg_split('/\s+/', trim((string) ($input['user_name'] ?? '')), 2); return [ 'order_type' => 'RECHARGE', 'platform_order_id' => (string) $input['order_sn'], 'currency' => strtoupper($config['currency'] ?? 'USD'), 'amount' => number_format((float) $input['amount'], 2, '.', ''), 'payment_method' => $methodMap[$payMethod] ?? 8, 'return_url' => $config['return'] ?? '', 'cancel_url' => $config['cancel'] ?? ($config['return'] ?? ''), 'connection_info' => [ 'ip' => $input['buy_ip'] ?: '0.0.0.0', 'country' => $config['country'] ?? 'US', 'state' => $config['state'] ?? 'NY', 'zip_code' => $config['zip'] ?? '00000', 'media_source' => $config['media_source'] ?? 'organic', 'language' => $config['language'] ?? 'en-US', ], 'account_info' => [ 'merchant_user_id' => (string) $input['user_id'], 'create_time' => time()*1000, 'role' => 'PRIVATE', 'email' => $this->emailOrDefault($input['user_email'] ?? '', $input['user_id']), 'phone' => preg_replace('/\D+/', '', (string) ($input['user_phone'] ?? '')) ?: '0000000000', 'area_code' => $config['area_code'] ?? '1', 'first_name' => chr(mt_rand(65, 90)) . 'user', 'last_name' => chr(mt_rand(65, 90)) . 'user', 'vip_level' => 0, ], ]; } public function notify(array $post) { $orderSn = $post['data']['platform_order_id'] ?? ''; if ($orderSn === '') { return 'success'; } $order = DB::connection('write') ->table('agent.dbo.order') ->where('order_sn', $orderSn) ->first(); if (!$order || !empty($order->pay_at) || !empty($order->finished_at)) { return 'success'; } $data = $post['data'] ?? []; $body = [ 'payment_sn' => $data['order_id'] ?? '', 'updated_at' => date('Y-m-d H:i:s'), ]; if ($this->isSuccessfulPayment($post)) { if (!$this->confirmSuccessfulPayment($post)) { Util::WriteLog('PayPlus', 'PayPlus order query not successful: ' . json_encode($post)); return 'success'; } $AdId = $order->AdId ?: ''; $eventType = $order->eventType ?: ''; $payAmount = round((float) ($data['amount'] ?? 0), 2); $body['pay_status'] = 1; $body['pay_at'] = date('Y-m-d H:i:s'); $body['finished_at'] = date('Y-m-d H:i:s'); $body['amount'] = (int) round($payAmount * NumConfig::NUM_VALUE); $config = (new PayConfig())->getConfig('PayPlus'); // 根据支付方式计算代收手续费: 费率% * 金额 + 固定$ // pay_rate 格式: [1=>[10,0.3], 2=>[13,0.3], 4=>[11,0.3], 8=>[12,0.3]] $payRates = $config['pay_rate'] ?? null; if (is_array($payRates)) { $payMethod = $order->order_title ?? 1; $payRate = $payRates[$payMethod] ?? ($payRates[1] ?? [10, 0.3]); $feePercent = $payRate[0] ?? 10; $feeFixed = $payRate[1] ?? 0.3; $body['payment_fee'] = intval(($body['amount'] * $feePercent) / 100) + (int)($feeFixed * NumConfig::NUM_VALUE); } $service = new OrderServices(); list($give, $favorablePrice, $recharge, $czReason, $cjReason) = $service->getPayInfo( $order->GiftsID ?: '', $order->user_id ?: '', $payAmount ); list($score) = $service->addRecord( $order->user_id, $payAmount, $favorablePrice, $orderSn, $order->GiftsID, $recharge, $czReason, $give, $cjReason, $order->AdId ?: '', $order->eventType ?: '', $body['payment_fee'] ?? 0 ); Order::dispatch([ $order->user_id, $payAmount, $score, $favorablePrice, $order->GiftsID, $orderSn, ]); } else { return 'success'; } DB::connection('write') ->table('agent.dbo.order') ->where('order_sn', $orderSn) ->update($body); return 'success'; } public function isSuccessfulPayment(array $post) { return ($post['event'] ?? '') === 'PAYMENT.CAPTURE.COMPLETED' && ($post['event_detail_name'] ?? '') === 'payment_captured' && (int) ($post['data']['order_status'] ?? 0) === 3; } public function confirmSuccessfulPayment(array $post) { $data = $post['data'] ?? []; $platformOrderId = $data['platform_order_id'] ?? ''; if ($platformOrderId === '') { return false; } try { $result = $this->service->queryPayinOrder($platformOrderId, $data['order_id'] ?? ''); } catch (\Exception $exception) { Util::WriteLog('PayPlus_error', 'PayPlus query order failed: ' . $exception->getMessage()); return false; } return $this->isSuccessfulPaymentQueryResult($result); } public function isSuccessfulPaymentQueryResult(array $result) { $data = $result['decryptedComponentDelta'] ?? ($result['data'] ?? $result); return (int) ($data['order_status'] ?? 0) === 3; } public function isFailedPayment(array $post) { $event = $post['event'] ?? ''; $detail = $post['event_detail_name'] ?? ''; $status = (int) ($post['data']['order_status'] ?? 0); return ($event === 'PAYMENT.CAPTURE.COMPLETED' && in_array($status, [4, 10, 11], true)) || $event === 'PAYMENT.ORDER.TIMEOUT' || in_array($detail, ['payment_declined', 'payment_timeout'], true); } protected function resolvePaymentUrl(array $result) { $data = $result['data'] ?? $result; foreach (['cashierUrl', 'cashier_url', 'payment_url', 'paymentUrl', 'redirect_url', 'url'] as $key) { if (!empty($data[$key])) { return $data[$key]; } } return ''; } protected function emailOrDefault($email, $userId) { return filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : 'unknown' . $userId . '@example.com'; } protected function stringOrDefault($value, $default) { $value = trim((string) $value); return $value === '' ? $default : $value; } }