@djangocfg/ext-payments 1.0.14 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.cjs +5 -8
- package/dist/config.js +5 -8
- package/dist/index.cjs +1906 -1043
- package/dist/index.d.cts +644 -59
- package/dist/index.d.ts +644 -59
- package/dist/index.js +1886 -1040
- package/package.json +13 -16
- package/src/WalletPage.tsx +100 -0
- package/src/api/generated/ext_payments/CLAUDE.md +10 -4
- package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +268 -5
- package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +102 -3
- package/src/api/generated/ext_payments/_utils/schemas/Balance.schema.ts +1 -1
- package/src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts +24 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateRequest.schema.ts +21 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentDetail.schema.ts +3 -3
- package/src/api/generated/ext_payments/_utils/schemas/PaymentList.schema.ts +2 -2
- package/src/api/generated/ext_payments/_utils/schemas/Transaction.schema.ts +1 -1
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCancelResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateRequest.schema.ts +21 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +42 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +29 -0
- package/src/api/generated/ext_payments/_utils/schemas/index.ts +8 -0
- package/src/api/generated/ext_payments/client.ts +1 -1
- package/src/api/generated/ext_payments/enums.ts +36 -0
- package/src/api/generated/ext_payments/ext_payments__payments/client.ts +104 -6
- package/src/api/generated/ext_payments/ext_payments__payments/models.ts +168 -8
- package/src/api/generated/ext_payments/index.ts +1 -1
- package/src/api/generated/ext_payments/schema.json +752 -42
- package/src/components/ActivityItem.tsx +118 -0
- package/src/components/ActivityList.tsx +93 -0
- package/src/components/AddFundsSheet.tsx +342 -0
- package/src/components/BalanceHero.tsx +102 -0
- package/src/components/CurrencyCombobox.tsx +49 -0
- package/src/components/PaymentSheet.tsx +352 -0
- package/src/components/WithdrawSheet.tsx +355 -0
- package/src/components/WithdrawalSheet.tsx +332 -0
- package/src/components/index.ts +11 -0
- package/src/config.ts +1 -0
- package/src/contexts/WalletContext.tsx +356 -0
- package/src/contexts/index.ts +13 -42
- package/src/contexts/types.ts +43 -37
- package/src/hooks/index.ts +3 -20
- package/src/hooks/useCurrencyOptions.ts +79 -0
- package/src/hooks/useEstimate.ts +113 -0
- package/src/hooks/useWithdrawalEstimate.ts +117 -0
- package/src/index.ts +9 -18
- package/src/types/index.ts +78 -0
- package/src/utils/errors.ts +36 -0
- package/src/utils/format.ts +65 -0
- package/src/utils/index.ts +3 -0
- package/src/contexts/BalancesContext.tsx +0 -63
- package/src/contexts/CurrenciesContext.tsx +0 -64
- package/src/contexts/OverviewContext.tsx +0 -173
- package/src/contexts/PaymentsContext.tsx +0 -122
- package/src/contexts/PaymentsExtensionProvider.tsx +0 -56
- package/src/contexts/README.md +0 -201
- package/src/contexts/RootPaymentsContext.tsx +0 -66
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -90
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -274
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -287
- package/src/layouts/PaymentsLayout/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/events.ts +0 -47
- package/src/layouts/PaymentsLayout/index.ts +0 -16
- package/src/layouts/PaymentsLayout/types.ts +0 -6
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -121
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -139
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -21
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -279
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -18
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -260
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -18
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Currency Combobox with TokenIcon
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Combobox, TokenIcon } from '@djangocfg/ui-core';
|
|
8
|
+
import type { CurrencyOption } from '../types';
|
|
9
|
+
|
|
10
|
+
interface CurrencyComboboxProps {
|
|
11
|
+
options: CurrencyOption[];
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: (value: string) => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function CurrencyCombobox({
|
|
20
|
+
options,
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
disabled,
|
|
24
|
+
placeholder = 'Select currency...',
|
|
25
|
+
}: CurrencyComboboxProps) {
|
|
26
|
+
return (
|
|
27
|
+
<Combobox
|
|
28
|
+
options={options}
|
|
29
|
+
value={value}
|
|
30
|
+
onValueChange={onChange}
|
|
31
|
+
placeholder={placeholder}
|
|
32
|
+
searchPlaceholder="Search..."
|
|
33
|
+
disabled={disabled}
|
|
34
|
+
className="h-14"
|
|
35
|
+
renderOption={(option) => (
|
|
36
|
+
<div className="flex items-center gap-3 flex-1">
|
|
37
|
+
<TokenIcon symbol={option.value} size={24} />
|
|
38
|
+
<span className="font-medium">{option.label}</span>
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
renderValue={(option) => option && (
|
|
42
|
+
<div className="flex items-center gap-3">
|
|
43
|
+
<TokenIcon symbol={option.value} size={24} />
|
|
44
|
+
<span className="font-medium">{option.label}</span>
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Sheet (Apple-style)
|
|
3
|
+
*
|
|
4
|
+
* Responsive: Dialog on desktop, Drawer on mobile
|
|
5
|
+
* Shows payment details with QR code and address
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use client';
|
|
9
|
+
|
|
10
|
+
import { useEffect, useState, useMemo } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
AlertCircle,
|
|
13
|
+
CheckCircle2,
|
|
14
|
+
Clock,
|
|
15
|
+
ExternalLink,
|
|
16
|
+
RefreshCw,
|
|
17
|
+
XCircle,
|
|
18
|
+
} from 'lucide-react';
|
|
19
|
+
import moment from 'moment';
|
|
20
|
+
import useSWR from 'swr';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
Button,
|
|
24
|
+
CopyButton,
|
|
25
|
+
Skeleton,
|
|
26
|
+
TokenIcon,
|
|
27
|
+
ResponsiveSheet,
|
|
28
|
+
ResponsiveSheetContent,
|
|
29
|
+
ResponsiveSheetDescription,
|
|
30
|
+
ResponsiveSheetHeader,
|
|
31
|
+
ResponsiveSheetTitle,
|
|
32
|
+
} from '@djangocfg/ui-core';
|
|
33
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
34
|
+
|
|
35
|
+
import { useWallet } from '../contexts/WalletContext';
|
|
36
|
+
import type { PaymentDetail } from '../api/generated/ext_payments/_utils/schemas';
|
|
37
|
+
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
// Props
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
interface PaymentSheetProps {
|
|
43
|
+
paymentId: string | null;
|
|
44
|
+
open: boolean;
|
|
45
|
+
onOpenChange: (open: boolean) => void;
|
|
46
|
+
onCreateNew?: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// Status Config
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
const statusConfig: Record<string, { icon: any; color: string; bg: string; label: string; animate?: boolean }> = {
|
|
54
|
+
pending: {
|
|
55
|
+
icon: Clock,
|
|
56
|
+
color: 'text-yellow-500',
|
|
57
|
+
bg: 'bg-yellow-500/10',
|
|
58
|
+
label: 'Waiting for payment',
|
|
59
|
+
},
|
|
60
|
+
confirming: {
|
|
61
|
+
icon: RefreshCw,
|
|
62
|
+
color: 'text-blue-500',
|
|
63
|
+
bg: 'bg-blue-500/10',
|
|
64
|
+
label: 'Confirming',
|
|
65
|
+
animate: true,
|
|
66
|
+
},
|
|
67
|
+
completed: {
|
|
68
|
+
icon: CheckCircle2,
|
|
69
|
+
color: 'text-green-500',
|
|
70
|
+
bg: 'bg-green-500/10',
|
|
71
|
+
label: 'Completed',
|
|
72
|
+
},
|
|
73
|
+
failed: {
|
|
74
|
+
icon: XCircle,
|
|
75
|
+
color: 'text-red-500',
|
|
76
|
+
bg: 'bg-red-500/10',
|
|
77
|
+
label: 'Failed',
|
|
78
|
+
},
|
|
79
|
+
expired: {
|
|
80
|
+
icon: AlertCircle,
|
|
81
|
+
color: 'text-muted-foreground',
|
|
82
|
+
bg: 'bg-muted',
|
|
83
|
+
label: 'Expired',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
// Component
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: PaymentSheetProps) {
|
|
92
|
+
const { getPaymentDetails } = useWallet();
|
|
93
|
+
const [timeLeft, setTimeLeft] = useState<string>('');
|
|
94
|
+
|
|
95
|
+
// Fetch payment details when sheet is open
|
|
96
|
+
const { data: payment, isLoading, error, mutate } = useSWR<PaymentDetail>(
|
|
97
|
+
open && paymentId ? ['payment-details', paymentId] : null,
|
|
98
|
+
() => getPaymentDetails(paymentId!),
|
|
99
|
+
{ refreshInterval: 10000 }
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Calculate time left
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (!payment?.expires_at) return;
|
|
105
|
+
|
|
106
|
+
const updateTimeLeft = () => {
|
|
107
|
+
const now = moment();
|
|
108
|
+
const expires = moment.utc(payment.expires_at!);
|
|
109
|
+
const diff = expires.diff(now);
|
|
110
|
+
|
|
111
|
+
if (diff <= 0) {
|
|
112
|
+
setTimeLeft('Expired');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const duration = moment.duration(diff);
|
|
117
|
+
const hours = Math.floor(duration.asHours());
|
|
118
|
+
const minutes = duration.minutes();
|
|
119
|
+
const seconds = duration.seconds();
|
|
120
|
+
|
|
121
|
+
setTimeLeft(`${hours}h ${minutes}m ${seconds}s`);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
updateTimeLeft();
|
|
125
|
+
const interval = setInterval(updateTimeLeft, 1000);
|
|
126
|
+
return () => clearInterval(interval);
|
|
127
|
+
}, [payment?.expires_at]);
|
|
128
|
+
|
|
129
|
+
// Prepare all display data before render
|
|
130
|
+
const displayData = useMemo(() => {
|
|
131
|
+
// Map status
|
|
132
|
+
const s = payment?.status?.toLowerCase();
|
|
133
|
+
let status: string;
|
|
134
|
+
if (s === 'completed' || s === 'success' || s === 'finished') status = 'completed';
|
|
135
|
+
else if (s === 'confirming' || s === 'partially_paid') status = 'confirming';
|
|
136
|
+
else if (s === 'expired') status = 'expired';
|
|
137
|
+
else if (s === 'failed' || s === 'error' || s === 'cancelled') status = 'failed';
|
|
138
|
+
else status = 'pending';
|
|
139
|
+
|
|
140
|
+
const config = statusConfig[status];
|
|
141
|
+
const isPending = status === 'pending';
|
|
142
|
+
const isExpired = status === 'expired' || timeLeft === 'Expired';
|
|
143
|
+
const isCompleted = status === 'completed';
|
|
144
|
+
const isFailed = status === 'failed';
|
|
145
|
+
const isConfirming = status === 'confirming';
|
|
146
|
+
const canPay = isPending && !isExpired;
|
|
147
|
+
|
|
148
|
+
// Description text
|
|
149
|
+
let description = '';
|
|
150
|
+
if (canPay) description = 'Send cryptocurrency to complete payment';
|
|
151
|
+
else if (isExpired) description = 'This payment has expired';
|
|
152
|
+
else if (isCompleted) description = 'Payment completed successfully';
|
|
153
|
+
else if (isFailed) description = 'Payment failed';
|
|
154
|
+
else if (isConfirming) description = 'Confirming your payment';
|
|
155
|
+
|
|
156
|
+
// Status badge
|
|
157
|
+
const statusBadge = {
|
|
158
|
+
bg: isExpired ? 'bg-muted' : config.bg,
|
|
159
|
+
iconColor: isExpired ? 'text-muted-foreground' : config.color,
|
|
160
|
+
iconAnimate: config.animate,
|
|
161
|
+
label: isExpired ? 'Payment Expired' : config.label,
|
|
162
|
+
subtitle: canPay && timeLeft
|
|
163
|
+
? `Expires in ${timeLeft}`
|
|
164
|
+
: isExpired
|
|
165
|
+
? 'Please create a new payment to continue'
|
|
166
|
+
: null,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// QR code URL
|
|
170
|
+
const qrCodeUrl = payment?.pay_address && canPay
|
|
171
|
+
? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}`
|
|
172
|
+
: null;
|
|
173
|
+
|
|
174
|
+
// Formatted values
|
|
175
|
+
const amountUsd = payment?.amount_usd ? `$${parseFloat(payment.amount_usd).toFixed(2)} USD` : '';
|
|
176
|
+
const createdAt = payment?.created_at
|
|
177
|
+
? moment.utc(payment.created_at).local().format('MMM D, YYYY HH:mm')
|
|
178
|
+
: '';
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
status,
|
|
182
|
+
config,
|
|
183
|
+
isPending,
|
|
184
|
+
isExpired,
|
|
185
|
+
isCompleted,
|
|
186
|
+
isFailed,
|
|
187
|
+
isConfirming,
|
|
188
|
+
canPay,
|
|
189
|
+
description,
|
|
190
|
+
statusBadge,
|
|
191
|
+
qrCodeUrl,
|
|
192
|
+
amountUsd,
|
|
193
|
+
createdAt,
|
|
194
|
+
};
|
|
195
|
+
}, [payment, timeLeft]);
|
|
196
|
+
|
|
197
|
+
const { config, canPay, isExpired, description, statusBadge, qrCodeUrl, amountUsd, createdAt } = displayData;
|
|
198
|
+
const StatusIcon = config.icon;
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<ResponsiveSheet open={open} onOpenChange={onOpenChange}>
|
|
202
|
+
<ResponsiveSheetContent className="sm:max-w-lg">
|
|
203
|
+
<ResponsiveSheetHeader>
|
|
204
|
+
<ResponsiveSheetTitle>Payment Details</ResponsiveSheetTitle>
|
|
205
|
+
<ResponsiveSheetDescription>{description}</ResponsiveSheetDescription>
|
|
206
|
+
</ResponsiveSheetHeader>
|
|
207
|
+
|
|
208
|
+
<div className="p-4 sm:p-0 sm:mt-4 overflow-y-auto max-h-[70vh]">
|
|
209
|
+
{isLoading && (
|
|
210
|
+
<div className="space-y-6">
|
|
211
|
+
<div className="flex items-center justify-center">
|
|
212
|
+
<Skeleton className="h-48 w-48 rounded-xl" />
|
|
213
|
+
</div>
|
|
214
|
+
<Skeleton className="h-12 w-full" />
|
|
215
|
+
<Skeleton className="h-24 w-full" />
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
|
|
219
|
+
{error && (
|
|
220
|
+
<div className="flex flex-col items-center justify-center py-12">
|
|
221
|
+
<XCircle className="h-12 w-12 text-destructive mb-4" />
|
|
222
|
+
<p className="text-sm text-muted-foreground mb-4">Failed to load payment</p>
|
|
223
|
+
<Button onClick={() => mutate()}>Try Again</Button>
|
|
224
|
+
</div>
|
|
225
|
+
)}
|
|
226
|
+
|
|
227
|
+
{payment && !isLoading && (
|
|
228
|
+
<div className="space-y-6">
|
|
229
|
+
{/* Status Badge */}
|
|
230
|
+
<div className={cn('flex items-center gap-3 p-4 rounded-xl', statusBadge.bg)}>
|
|
231
|
+
<StatusIcon className={cn('h-6 w-6', statusBadge.iconColor, statusBadge.iconAnimate && 'animate-spin')} />
|
|
232
|
+
<div className="flex-1">
|
|
233
|
+
<div className="font-semibold">{statusBadge.label}</div>
|
|
234
|
+
{statusBadge.subtitle && (
|
|
235
|
+
<div className="text-sm text-muted-foreground">{statusBadge.subtitle}</div>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Amount */}
|
|
241
|
+
<div className="bg-muted rounded-xl p-4 space-y-3">
|
|
242
|
+
<div className="flex items-center justify-between">
|
|
243
|
+
<span className="text-muted-foreground">Amount to send</span>
|
|
244
|
+
<div className="flex items-center gap-2">
|
|
245
|
+
<TokenIcon symbol={payment.currency_code} size={24} />
|
|
246
|
+
<span className="font-mono font-bold text-lg">
|
|
247
|
+
{payment.pay_amount} {payment.currency_code}
|
|
248
|
+
</span>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
<div className="flex items-center justify-between text-sm">
|
|
252
|
+
<span className="text-muted-foreground">Equivalent</span>
|
|
253
|
+
<span className="font-semibold">{amountUsd}</span>
|
|
254
|
+
</div>
|
|
255
|
+
{payment.currency_network && (
|
|
256
|
+
<div className="flex items-center justify-between text-sm pt-2 border-t">
|
|
257
|
+
<span className="text-muted-foreground">Network</span>
|
|
258
|
+
<span className="font-medium">{payment.currency_network}</span>
|
|
259
|
+
</div>
|
|
260
|
+
)}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
{/* QR Code */}
|
|
264
|
+
{qrCodeUrl && (
|
|
265
|
+
<div className="flex justify-center p-6 bg-white rounded-xl">
|
|
266
|
+
<img src={qrCodeUrl} alt="Payment QR Code" className="w-48 h-48" />
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
|
|
270
|
+
{/* Payment Address */}
|
|
271
|
+
{payment.pay_address && canPay && (
|
|
272
|
+
<div className="space-y-2">
|
|
273
|
+
<label className="text-sm font-medium">Payment Address</label>
|
|
274
|
+
<div className="flex items-center gap-2">
|
|
275
|
+
<div className="flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all">
|
|
276
|
+
{payment.pay_address}
|
|
277
|
+
</div>
|
|
278
|
+
<CopyButton value={payment.pay_address} variant="outline" className="shrink-0" />
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{/* Expired - Create New Payment */}
|
|
284
|
+
{isExpired && onCreateNew && (
|
|
285
|
+
<Button
|
|
286
|
+
size="lg"
|
|
287
|
+
className="w-full"
|
|
288
|
+
onClick={() => {
|
|
289
|
+
onOpenChange(false);
|
|
290
|
+
onCreateNew();
|
|
291
|
+
}}
|
|
292
|
+
>
|
|
293
|
+
Create New Payment
|
|
294
|
+
</Button>
|
|
295
|
+
)}
|
|
296
|
+
|
|
297
|
+
{/* Transaction Hash (if completed) */}
|
|
298
|
+
{payment.transaction_hash && (
|
|
299
|
+
<div className="space-y-2">
|
|
300
|
+
<label className="text-sm font-medium">Transaction Hash</label>
|
|
301
|
+
<div className="p-3 bg-muted rounded-xl font-mono text-sm break-all">
|
|
302
|
+
{payment.transaction_hash}
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
|
|
307
|
+
{/* External Link */}
|
|
308
|
+
{payment.payment_url && canPay && (
|
|
309
|
+
<Button
|
|
310
|
+
variant="outline"
|
|
311
|
+
className="w-full"
|
|
312
|
+
onClick={() => window.open(payment.payment_url!, '_blank')}
|
|
313
|
+
>
|
|
314
|
+
<ExternalLink className="h-4 w-4 mr-2" />
|
|
315
|
+
Open in Payment Provider
|
|
316
|
+
</Button>
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
{/* Metadata */}
|
|
320
|
+
<div className="space-y-2 text-xs text-muted-foreground pt-4 border-t">
|
|
321
|
+
<div className="flex justify-between">
|
|
322
|
+
<span>Payment ID</span>
|
|
323
|
+
<span className="font-mono">{payment.id}</span>
|
|
324
|
+
</div>
|
|
325
|
+
{payment.internal_payment_id && (
|
|
326
|
+
<div className="flex justify-between">
|
|
327
|
+
<span>Order #</span>
|
|
328
|
+
<span className="font-mono">{payment.internal_payment_id}</span>
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
<div className="flex justify-between">
|
|
332
|
+
<span>Created</span>
|
|
333
|
+
<span>{createdAt}</span>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
{/* Refresh Button */}
|
|
338
|
+
<Button
|
|
339
|
+
variant="ghost"
|
|
340
|
+
className="w-full"
|
|
341
|
+
onClick={() => mutate()}
|
|
342
|
+
>
|
|
343
|
+
<RefreshCw className="h-4 w-4 mr-2" />
|
|
344
|
+
Refresh Status
|
|
345
|
+
</Button>
|
|
346
|
+
</div>
|
|
347
|
+
)}
|
|
348
|
+
</div>
|
|
349
|
+
</ResponsiveSheetContent>
|
|
350
|
+
</ResponsiveSheet>
|
|
351
|
+
);
|
|
352
|
+
}
|