feat: integrate Stripe payment with bugfixes and active timeout cancellation
- Add Stripe payment provider with Checkout Session flow - Payment provider abstraction layer (EasyPay + Stripe unified interface) - Stripe webhook with proper raw body handling and signature verification - Frontend: Stripe button with URL validation, anti-duplicate click, noopener - Active timeout cancellation: query platform before expiring, recover paid orders - Singleton Stripe client, idempotency keys, Math.round for amounts - Handle async_payment events, return null for unknown webhook events - Set Checkout Session expires_at aligned with order timeout - Add cancelPayment to provider interface (Stripe: sessions.expire, EasyPay: no-op) - Enable stripe in frontend payment type list
This commit is contained in:
@@ -55,14 +55,14 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail }:
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 bg-white">
|
||||
{orders.map((order) => {
|
||||
const statusInfo = STATUS_LABELS[order.status] || { label: order.status, className: 'bg-gray-100 text-gray-800' };
|
||||
const statusInfo = STATUS_LABELS[order.status] || {
|
||||
label: order.status,
|
||||
className: 'bg-gray-100 text-gray-800',
|
||||
};
|
||||
return (
|
||||
<tr key={order.id} className="hover:bg-gray-50">
|
||||
<td className="whitespace-nowrap px-4 py-3 text-sm">
|
||||
<button
|
||||
onClick={() => onViewDetail(order.id)}
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
<button onClick={() => onViewDetail(order.id)} className="text-blue-600 hover:underline">
|
||||
{order.id.slice(0, 12)}...
|
||||
</button>
|
||||
</td>
|
||||
@@ -70,9 +70,7 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail }:
|
||||
<div>{order.userName || '-'}</div>
|
||||
<div className="text-xs text-gray-400">{order.userEmail || `ID: ${order.userId}`}</div>
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-sm font-medium">
|
||||
¥{order.amount.toFixed(2)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-sm font-medium">¥{order.amount.toFixed(2)}</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-sm">
|
||||
<span className={`inline-flex rounded-full px-2 py-1 text-xs font-semibold ${statusInfo.className}`}>
|
||||
{statusInfo.label}
|
||||
@@ -109,9 +107,7 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail }:
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{orders.length === 0 && (
|
||||
<div className="py-12 text-center text-gray-500">暂无订单</div>
|
||||
)}
|
||||
{orders.length === 0 && <div className="py-12 text-center text-gray-500">暂无订单</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user