$orderNo, 'status' => 'cancelled']); } public function showOrder(string $orderNo): void { $u = Auth::require(); $order = PayService::findByOrderNoForUser($orderNo, $u['id']); if ($order === null) { Json::fail('not_found', '订单不存在', 404); } Json::ok([ 'order_no' => $order['order_no'], 'status' => $order['status'], 'amount_cents' => (int)$order['amount_cents'], 'channel' => $order['channel'], 'paid_at' => $order['paid_at'], 'refund_status' => $order['refund_status'] ?? 'none', 'refund_reason' => $order['refund_reason'] ?? null, ]); } public function requestRefund(string $orderNo): void { $u = Auth::require(); $body = Json::readBody(); $reason = trim((string)($body['reason'] ?? '')); PayService::requestRefund($u['id'], $orderNo, $reason); Json::ok(['order_no' => $orderNo, 'refund_status' => 'pending']); } public function alipayNotify(): void { $raw = file_get_contents('php://input') ?: ''; parse_str($raw, $params); if (empty($params) && !empty($_POST)) { $params = $_POST; } if (!AlipayClient::verifyNotify($params)) { http_response_code(400); echo 'fail'; exit; } $status = (string)($params['trade_status'] ?? ''); if (!in_array($status, ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) { echo 'success'; exit; } $orderNo = (string)($params['out_trade_no'] ?? ''); $tradeNo = (string)($params['trade_no'] ?? ''); $amountCents = (int)round((float)($params['total_amount'] ?? 0) * 100); if (PayService::markPaid($orderNo, 'alipay', $tradeNo, $amountCents)) { echo 'success'; } else { echo 'fail'; } exit; } public function wechatNotify(): void { $body = file_get_contents('php://input') ?: ''; $signature = $_SERVER['HTTP_WEIXINPAY_SIGNATURE'] ?? $_SERVER['HTTP_WEIXINPAY2_SIGNATURE'] ?? ''; $serial = $_SERVER['HTTP_WEIXINPAY_SERIAL'] ?? $_SERVER['HTTP_WEIXINPAY2_SERIAL'] ?? ''; $timestamp = $_SERVER['HTTP_WEIXINPAY_TIMESTAMP'] ?? $_SERVER['HTTP_WEIXINPAY2_TIMESTAMP'] ?? ''; $nonce = $_SERVER['HTTP_WEIXINPAY_NONCE'] ?? $_SERVER['HTTP_WEIXINPAY2_NONCE'] ?? ''; if (!WeChatClient::verifyNotify($body, $signature, $serial, $timestamp, $nonce)) { http_response_code(401); header('Content-Type: application/json'); echo json_encode(['code' => 'FAIL', 'message' => '验签失败']); exit; } $data = json_decode($body, true); $plain = WeChatClient::decryptResource( (string)($data['resource']['ciphertext'] ?? ''), (string)($data['resource']['associated_data'] ?? ''), (string)($data['resource']['nonce'] ?? ''), (string)\Soon\Api\Core\Config::get('wechat.api_v3_key', '') ); if ($plain === null) { http_response_code(400); header('Content-Type: application/json'); echo json_encode(['code' => 'FAIL', 'message' => '解密失败']); exit; } $decoded = json_decode($plain, true); $orderNo = (string)($decoded['out_trade_no'] ?? ''); $txnId = (string)($decoded['transaction_id'] ?? ''); $amountCents = (int)($decoded['amount']['total'] ?? 0); $tradeState = (string)($decoded['trade_state'] ?? ''); if ($tradeState !== '' && $tradeState !== 'SUCCESS') { header('Content-Type: application/json'); echo json_encode(['code' => 'SUCCESS']); exit; } if (!PayService::markPaid($orderNo, 'wechat', $txnId, $amountCents)) { http_response_code(400); header('Content-Type: application/json'); echo json_encode(['code' => 'FAIL', 'message' => '订单处理失败']); exit; } header('Content-Type: application/json'); echo json_encode(['code' => 'SUCCESS']); exit; } }