feat: 更新支付模块 (Stripe/PayPal/PingPong) 和 uni-app 配置
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
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}")
|
||||
Reference in New Issue
Block a user