get_config(); return !empty($config['secret_key']) && !empty($config['publishable_key']); } public function create_payment(array $order): array { if (!$this->is_configured()) { return ['success' => false, 'message' => __('Stripe 未配置', 'aksrt')]; } $config = $this->get_config(); $order_number = $order['order_number'] ?? ''; $amount = (float) ($order['amount'] ?? 0); $subject = $order['subject'] ?? __('订单支付', 'aksrt'); $currency = strtolower($config['currency'] ?? 'usd'); try { $stripe = $this->create_client($config); // 创建 Checkout Session $session_params = [ 'payment_method_types' => $this->get_payment_methods($config), 'line_items' => [[ 'price_data' => [ 'currency' => $currency, 'product_data' => [ 'name' => $subject, ], 'unit_amount' => $this->to_smallest_unit($amount, $currency), ], 'quantity' => 1, ]], 'mode' => 'payment', 'success_url' => home_url('/wp-json/aksrt/v1/payment/stripe/return?session_id={CHECKOUT_SESSION_ID}&order=' . $order_number), 'cancel_url' => home_url('/wp-json/aksrt/v1/payment/stripe/cancel?order=' . $order_number), 'metadata' => [ 'order_number' => $order_number, ], ]; // 启用自动支付方式(Apple Pay, Google Pay 等) if (!empty($config['auto_payment_methods'])) { $session_params['automatic_payment_methods'] = [ 'enabled' => true, ]; } $session = \Stripe\Checkout\Session::create($session_params, [ 'api_key' => $config['secret_key'], ]); return [ 'success' => true, 'payment_url' => $session->url, 'payment_data' => [ 'order_number' => $order_number, 'session_id' => $session->id, ], 'message' => __('Stripe 支付页面已创建', 'aksrt'), ]; } catch (\Stripe\Exception\ApiErrorException $e) { return ['success' => false, 'message' => $e->getMessage()]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } public function verify_callback(WP_REST_Request $request): array { $config = $this->get_config(); try { $stripe = $this->create_client($config); $payload = $request->get_body(); $sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? ''; // 验证 Webhook 签名 $event = \Stripe\Webhook::constructEvent( $payload, $sigHeader, $config['webhook_secret'] ?? '' ); if ($event->type !== 'checkout.session.completed') { return ['success' => false, 'message' => __('非支付完成事件', 'aksrt')]; } $session = $event->data->object; $orderNumber = $session->metadata->order_number ?? ''; $amount = ($session->amount_total ?? 0) / 100; return [ 'success' => true, 'order_number' => $orderNumber, 'amount' => $amount, 'transaction_id' => $session->payment_intent ?? $session->id, 'status' => $session->payment_status === 'paid' ? 'paid' : 'pending', ]; } catch (\Stripe\Exception\SignatureVerificationException $e) { return ['success' => false, 'message' => __('Webhook 签名验证失败', 'aksrt')]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } public function query_status(string $order_number): array { if (!$this->is_configured()) { return ['success' => false, 'message' => __('Stripe 未配置', 'aksrt')]; } $config = $this->get_config(); try { $stripe = $this->create_client($config); // 注意:Stripe API 的 Session::all() 不支持按 metadata 过滤。 // 此处通过创建时间倒序获取最近的 session,再匹配 metadata。 // 生产环境建议在 create_payment 时保存 session_id 并用 Session::retrieve() 查询。 $sessions = \Stripe\Checkout\Session::all([ 'limit' => 100, ], ['api_key' => $config['secret_key']]); // 手动按 metadata 匹配 $found = null; foreach ($sessions->data as $session) { if (($session->metadata->order_number ?? '') === $order_number) { $found = $session; break; } } if ($found) { $status = 'pending'; if ($found->payment_status === 'paid') { $status = 'paid'; } elseif ($found->status === 'expired') { $status = 'cancelled'; } return [ 'success' => true, 'status' => $status, 'transaction_id' => $found->payment_intent ?? '', ]; } return ['success' => true, 'status' => 'pending', 'message' => __('未找到支付记录', 'aksrt')]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } public function refund(string $order_number, float $amount): array { if (!$this->is_configured()) { return ['success' => false, 'message' => __('Stripe 未配置', 'aksrt')]; } $config = $this->get_config(); try { $stripe = $this->create_client($config); // 先查询 payment_intent(手动按 metadata 匹配) $sessions = \Stripe\Checkout\Session::all([ 'limit' => 100, ], ['api_key' => $config['secret_key']]); $found = null; foreach ($sessions->data as $session) { if (($session->metadata->order_number ?? '') === $order_number) { $found = $session; break; } } if (!$found) { return ['success' => false, 'message' => __('未找到支付记录', 'aksrt')]; } $paymentIntentId = $found->payment_intent; if (empty($paymentIntentId)) { return ['success' => false, 'message' => __('未找到支付意图', 'aksrt')]; } $currency = strtolower($config['currency'] ?? 'usd'); $refund = \Stripe\Refund::create([ 'payment_intent' => $paymentIntentId, 'amount' => $this->to_smallest_unit($amount, $currency), 'reason' => 'requested_by_customer', ], ['api_key' => $config['secret_key']]); return [ 'success' => true, 'refund_id' => $refund->id, 'message' => __('退款成功', 'aksrt'), ]; } catch (\Stripe\Exception\ApiErrorException $e) { return ['success' => false, 'message' => $e->getMessage()]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } /** * 获取 Stripe Publishable Key(前端使用)。 */ public function get_publishable_key(): string { $config = $this->get_config(); return $config['publishable_key'] ?? ''; } private function get_config(): array { // 优先从统一设置读取(schema 保存的 key) if (class_exists('AKSRT_Settings')) { $all = AKSRT_Settings::get_all(); if (!empty($all['stripe_secret_key'])) { return [ 'secret_key' => $all['stripe_secret_key'] ?? '', 'publishable_key' => $all['stripe_publishable_key'] ?? '', 'webhook_secret' => $all['stripe_webhook_secret'] ?? '', 'currency' => $all['stripe_currency'] ?? 'usd', 'mode' => !empty($all['stripe_test_mode']) ? 'test' : 'live', ]; } } // 回退到独立 option $settings = get_option('aksrt_payment_stripe', []); return is_array($settings) ? $settings : []; } private function create_client(array $config): void { \Stripe\Stripe::setApiKey($config['secret_key']); // 设置 API 版本 \Stripe\Stripe::setApiVersion('2023-10-16'); // 启用遥测(可选) if (!empty($config['enable_telemetry'])) { \Stripe\Stripe::setEnableTelemetry(true); } } /** * 获取支持的支付方式。 */ private function get_payment_methods(array $config): array { $methods = $config['payment_methods'] ?? ['card']; // 验证支付方式 $valid = ['card', 'alipay', 'wechat_pay', 'sepa_debit', 'ideal', 'bancontact', 'giropay', 'sofort']; return array_values(array_intersect($methods, $valid)) ?: ['card']; } /** * 将金额转换为最小单位(分/cent)。 */ private function to_smallest_unit(float $amount, string $currency): int { // 零小数货币(日元、韩元等) $zeroDecimal = ['bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga', 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf']; if (in_array(strtolower($currency), $zeroDecimal, true)) { return (int) $amount; } return (int) round($amount * 100); } }