Files
trade-assistant/backend/app/services/stripe_pay.py
T

124 lines
4.8 KiB
Python

import logging
import stripe
from typing import Optional, Dict, Any
from app.config import settings
from app.services.payment_gateway import PaymentGateway
logger = logging.getLogger(__name__)
class StripePaymentService(PaymentGateway):
name = "stripe"
supported_types = ["card", "alipay", "wechat"]
def __init__(self):
self.secret_key = settings.STRIPE_SECRET_KEY
self.webhook_secret = settings.STRIPE_WEBHOOK_SECRET
if self.secret_key:
stripe.api_key = self.secret_key
async def create_order(self, order_no: str, amount: int, description: str,
**kwargs) -> Dict[str, Any]:
if not self.secret_key:
raise ValueError("Stripe 未配置")
pay_type = kwargs.get("pay_type", "card")
success_url = kwargs.get("success_url", "")
cancel_url = kwargs.get("cancel_url", "")
payment_method_types = ["card"]
if pay_type == "alipay":
payment_method_types = ["alipay"]
elif pay_type == "wechat":
payment_method_types = ["wechat_pay"]
session = stripe.checkout.Session.create(
payment_method_types=payment_method_types,
line_items=[{
"price_data": {
"currency": "usd",
"product_data": {"name": description},
"unit_amount": amount,
},
"quantity": 1,
}],
mode="payment",
success_url=success_url or "https://trade.yuzhiran.com/workspace/credits?stripe=success",
cancel_url=cancel_url or "https://trade.yuzhiran.com/workspace/credits?stripe=cancel",
metadata={"order_no": order_no},
)
return {
"gateway_order_id": session.id,
"merchant_order_id": order_no,
"session_url": session.url,
"session_id": session.id,
"amount": amount / 100,
"status": "pending",
}
async def query_order(self, order_no: str) -> Dict[str, Any]:
try:
session = stripe.checkout.Session.retrieve(order_no)
return {
"status": session.status,
"payment_status": session.payment_status,
"amount": session.amount_total / 100 if session.amount_total else 0,
"currency": session.currency or "usd",
"customer_email": session.customer_details.email if session.customer_details else None,
}
except stripe.error.StripeError as e:
logger.error(f"Stripe query failed: {e}")
return {"status": "unknown"}
async def refund(self, order_no: str, amount: int, reason: str = "") -> Dict[str, Any]:
try:
payment_intents = stripe.checkout.Session.list(
payment_intent=True
)
refund = stripe.Refund.create(
payment_intent=order_no,
amount=amount,
reason="requested_by_customer",
)
return {"status": refund.status, "refund_id": refund.id}
except stripe.error.StripeError as e:
logger.error(f"Stripe refund failed: {e}")
raise ValueError(f"退款失败: {e}")
async def query_refund(self, order_no: str) -> Dict[str, Any]:
return {"status": "not_implemented"}
def verify_callback(self, headers: dict, body: str) -> bool:
if not self.webhook_secret:
logger.warning("Stripe webhook secret not configured")
return False
try:
sig_header = headers.get("stripe-signature", "")
stripe.Webhook.construct_event(body, sig_header, self.webhook_secret)
return True
except stripe.error.SignatureVerificationError as e:
logger.warning(f"Stripe webhook signature invalid: {e}")
return False
def parse_callback(self, body: str, headers: dict) -> Dict[str, Any]:
event = stripe.Webhook.construct_event(body, headers.get("stripe-signature", ""), self.webhook_secret)
session = event.data.object
return {
"event": event.type,
"order_no": session.get("metadata", {}).get("order_no", ""),
"gateway_order_id": session.get("id", ""),
"gateway_order_no": session.get("payment_intent", ""),
"amount": (session.get("amount_total", 0) or 0) / 100,
"success": event.type == "checkout.session.completed",
"raw": session,
}
async def close_order(self, order_no: str) -> Dict[str, Any]:
try:
stripe.checkout.Session.expire(order_no)
return {"status": "expired"}
except stripe.error.StripeError as e:
logger.error(f"Stripe close order failed: {e}")
raise ValueError(f"关闭订单失败: {e}")