@enomshop/paystack 1.0.7 → 1.0.9

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.
@@ -0,0 +1,345 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
+ import { Container, Heading, Label, Input, Textarea, Button, toast, Text, Table, Badge, StatusBadge } from "@medusajs/ui";
4
+ import { useState, useRef, useCallback, useEffect } from "react";
5
+ import { CurrencyDollar } from "@medusajs/icons";
6
+ import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar } from "recharts";
7
+ const ManualPaymentWidget = ({ data: order }) => {
8
+ const [amount, setAmount] = useState("");
9
+ const [reference, setReference] = useState("");
10
+ const [note, setNote] = useState("");
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const handleSubmit = async (e) => {
13
+ e.preventDefault();
14
+ setIsLoading(true);
15
+ try {
16
+ const res = await fetch(`/admin/orders/${order.id}/manual-payment`, {
17
+ method: "POST",
18
+ headers: {
19
+ "Content-Type": "application/json"
20
+ },
21
+ body: JSON.stringify({
22
+ amount: Number(amount),
23
+ reference,
24
+ note
25
+ })
26
+ });
27
+ if (!res.ok) {
28
+ const error = await res.json();
29
+ throw new Error(error.message || "Failed to record payment");
30
+ }
31
+ toast.success("Success", {
32
+ description: "Manual payment recorded successfully"
33
+ });
34
+ setAmount("");
35
+ setReference("");
36
+ setNote("");
37
+ window.location.reload();
38
+ } catch (err) {
39
+ toast.error("Error", {
40
+ description: err.message
41
+ });
42
+ } finally {
43
+ setIsLoading(false);
44
+ }
45
+ };
46
+ return /* @__PURE__ */ jsxs(Container, { className: "p-6 mt-4", children: [
47
+ /* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-4", children: "Record Manual Payment" }),
48
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [
49
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
50
+ /* @__PURE__ */ jsx(Label, { htmlFor: "amount", children: "Amount" }),
51
+ /* @__PURE__ */ jsx(
52
+ Input,
53
+ {
54
+ id: "amount",
55
+ type: "number",
56
+ step: "0.01",
57
+ value: amount,
58
+ onChange: (e) => setAmount(e.target.value),
59
+ required: true,
60
+ placeholder: "e.g. 50.00"
61
+ }
62
+ )
63
+ ] }),
64
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
65
+ /* @__PURE__ */ jsx(Label, { htmlFor: "reference", children: "Reference (Optional)" }),
66
+ /* @__PURE__ */ jsx(
67
+ Input,
68
+ {
69
+ id: "reference",
70
+ type: "text",
71
+ value: reference,
72
+ onChange: (e) => setReference(e.target.value),
73
+ placeholder: "e.g. Bank Transfer TXN-123"
74
+ }
75
+ )
76
+ ] }),
77
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
78
+ /* @__PURE__ */ jsx(Label, { htmlFor: "note", children: "Note (Optional)" }),
79
+ /* @__PURE__ */ jsx(
80
+ Textarea,
81
+ {
82
+ id: "note",
83
+ value: note,
84
+ onChange: (e) => setNote(e.target.value),
85
+ placeholder: "Additional details..."
86
+ }
87
+ )
88
+ ] }),
89
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-2", children: /* @__PURE__ */ jsx(Button, { type: "submit", variant: "primary", isLoading, children: "Record Payment" }) })
90
+ ] })
91
+ ] });
92
+ };
93
+ defineWidgetConfig({
94
+ zone: "order.details.after"
95
+ });
96
+ const PaymentHistoryWidget = ({ data: order }) => {
97
+ var _a;
98
+ const paymentCollection = (_a = order.payment_collections) == null ? void 0 : _a[0];
99
+ const payments = (paymentCollection == null ? void 0 : paymentCollection.payments) || [];
100
+ return /* @__PURE__ */ jsxs(Container, { className: "p-6 mt-4", children: [
101
+ /* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-4", children: "Payment History" }),
102
+ payments.length === 0 ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "No payments recorded yet." }) : /* @__PURE__ */ jsxs(Table, { children: [
103
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
104
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "ID" }),
105
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Provider" }),
106
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Amount" }),
107
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Status" }),
108
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Date" })
109
+ ] }) }),
110
+ /* @__PURE__ */ jsx(Table.Body, { children: payments.map((payment) => /* @__PURE__ */ jsxs(Table.Row, { children: [
111
+ /* @__PURE__ */ jsx(Table.Cell, { className: "font-mono text-xs", children: payment.id.slice(-8) }),
112
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { size: "small", color: payment.provider_id === "paystack" ? "blue" : "grey", children: payment.provider_id }) }),
113
+ /* @__PURE__ */ jsx(Table.Cell, { children: new Intl.NumberFormat("en-US", { style: "currency", currency: payment.currency_code }).format(payment.amount) }),
114
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { size: "small", color: payment.captured_at ? "green" : payment.canceled_at ? "red" : "orange", children: payment.captured_at ? "Captured" : payment.canceled_at ? "Canceled" : "Pending" }) }),
115
+ /* @__PURE__ */ jsx(Table.Cell, { children: new Date(payment.created_at).toLocaleDateString() })
116
+ ] }, payment.id)) })
117
+ ] })
118
+ ] });
119
+ };
120
+ defineWidgetConfig({
121
+ zone: "order.details.after"
122
+ });
123
+ const config = defineRouteConfig({
124
+ label: "Paystack",
125
+ icon: CurrencyDollar
126
+ });
127
+ function PaystackDashboard() {
128
+ var _a;
129
+ const [data, setData] = useState(null);
130
+ const [payments, setPayments] = useState([]);
131
+ const [loading, setLoading] = useState(true);
132
+ const [loadingMore, setLoadingMore] = useState(false);
133
+ const [page, setPage] = useState(1);
134
+ const [search, setSearch] = useState("");
135
+ const [hasMore, setHasMore] = useState(true);
136
+ const observer = useRef(null);
137
+ const lastElementRef = useCallback((node) => {
138
+ if (loading || loadingMore || !hasMore) return;
139
+ if (observer.current) observer.current.disconnect();
140
+ observer.current = new IntersectionObserver((entries) => {
141
+ if (entries[0].isIntersecting) {
142
+ setPage((prev) => prev + 1);
143
+ }
144
+ });
145
+ if (node) observer.current.observe(node);
146
+ }, [loading, loadingMore, hasMore]);
147
+ const fetchData = async (currentPage, currentSearch, isAppend = false) => {
148
+ if (isAppend) setLoadingMore(true);
149
+ else setLoading(true);
150
+ try {
151
+ const res = await fetch(`/admin/paystack/dashboard?page=${currentPage}&search=${encodeURIComponent(currentSearch)}`);
152
+ if (!res.ok) throw new Error("Failed to fetch");
153
+ const json = await res.json();
154
+ if (isAppend) {
155
+ setPayments((prev) => [...prev, ...json.payments]);
156
+ } else {
157
+ setData(json);
158
+ setPayments(json.payments);
159
+ }
160
+ setHasMore(json.has_more);
161
+ } catch (err) {
162
+ console.error("Error fetching Paystack dashboard data:", err);
163
+ } finally {
164
+ setLoading(false);
165
+ setLoadingMore(false);
166
+ }
167
+ };
168
+ useEffect(() => {
169
+ setPage(1);
170
+ const delayDebounceFn = setTimeout(() => {
171
+ fetchData(1, search, false);
172
+ }, 500);
173
+ return () => clearTimeout(delayDebounceFn);
174
+ }, [search]);
175
+ useEffect(() => {
176
+ if (page > 1) {
177
+ fetchData(page, search, true);
178
+ }
179
+ }, [page]);
180
+ useEffect(() => {
181
+ const interval = setInterval(() => {
182
+ if (page === 1 && !search) {
183
+ fetchData(1, "", false);
184
+ }
185
+ }, 5 * 60 * 1e3);
186
+ return () => clearInterval(interval);
187
+ }, [page, search]);
188
+ if (loading && page === 1 && !data) {
189
+ return /* @__PURE__ */ jsx(Container, { className: "p-8 flex items-center justify-center", children: /* @__PURE__ */ jsx(Text, { children: "Loading Paystack Dashboard..." }) });
190
+ }
191
+ const currencies = Object.keys((data == null ? void 0 : data.totals) || {});
192
+ const colors = ["#0ea5e9", "#10b981", "#f59e0b", "#8b5cf6"];
193
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
194
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
195
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Paystack Dashboard" }),
196
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Auto-refreshes every 5 mins" })
197
+ ] }),
198
+ !search && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: currencies.length === 0 ? /* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
199
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle mb-2", children: "Current Balance" }),
200
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "0.00" })
201
+ ] }) : currencies.map((currency) => {
202
+ var _a2;
203
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 col-span-1 md:col-span-2", children: [
204
+ /* @__PURE__ */ jsxs(Container, { className: "p-6 bg-ui-bg-base border-l-4 border-l-emerald-500", children: [
205
+ /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-subtle mb-2", children: [
206
+ "Current Balance (",
207
+ currency,
208
+ ")"
209
+ ] }),
210
+ /* @__PURE__ */ jsx(Heading, { level: "h1", className: "text-emerald-600 dark:text-emerald-400", children: new Intl.NumberFormat("en-US", {
211
+ style: "currency",
212
+ currency
213
+ }).format(((_a2 = data.balances) == null ? void 0 : _a2[currency]) || 0) })
214
+ ] }),
215
+ /* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
216
+ /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-subtle mb-2", children: [
217
+ "Total Received All-Time (",
218
+ currency,
219
+ ")"
220
+ ] }),
221
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: new Intl.NumberFormat("en-US", {
222
+ style: "currency",
223
+ currency
224
+ }).format(data.totals[currency]) })
225
+ ] })
226
+ ] }, currency);
227
+ }) }),
228
+ !search && /* @__PURE__ */ jsxs(Container, { className: "p-6 h-[400px]", children: [
229
+ /* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-6", children: "Revenue Over Time" }),
230
+ ((_a = data == null ? void 0 : data.chart_data) == null ? void 0 : _a.length) > 0 ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: data.chart_data, margin: { top: 10, right: 30, left: 0, bottom: 0 }, children: [
231
+ /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e5e7eb" }),
232
+ /* @__PURE__ */ jsx(XAxis, { dataKey: "name", axisLine: false, tickLine: false, tick: { fill: "#6b7280", fontSize: 12 }, dy: 10 }),
233
+ /* @__PURE__ */ jsx(YAxis, { axisLine: false, tickLine: false, tick: { fill: "#6b7280", fontSize: 12 }, dx: -10 }),
234
+ /* @__PURE__ */ jsx(
235
+ Tooltip,
236
+ {
237
+ cursor: { fill: "#f3f4f6" },
238
+ contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" }
239
+ }
240
+ ),
241
+ /* @__PURE__ */ jsx(Legend, { wrapperStyle: { paddingTop: "20px" } }),
242
+ currencies.map((currency, index) => /* @__PURE__ */ jsx(
243
+ Bar,
244
+ {
245
+ dataKey: currency,
246
+ fill: colors[index % colors.length],
247
+ radius: [4, 4, 0, 0],
248
+ maxBarSize: 50
249
+ },
250
+ currency
251
+ ))
252
+ ] }) }) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "No revenue data available yet." }) })
253
+ ] }),
254
+ /* @__PURE__ */ jsxs(Container, { className: "p-0 overflow-hidden", children: [
255
+ /* @__PURE__ */ jsxs("div", { className: "p-6 border-b border-ui-border-base flex items-center justify-between", children: [
256
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Payment History" }),
257
+ /* @__PURE__ */ jsx("div", { className: "w-64", children: /* @__PURE__ */ jsx(
258
+ Input,
259
+ {
260
+ type: "search",
261
+ placeholder: "Search Order ID or Reference...",
262
+ value: search,
263
+ onChange: (e) => setSearch(e.target.value)
264
+ }
265
+ ) })
266
+ ] }),
267
+ /* @__PURE__ */ jsxs(Table, { children: [
268
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
269
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Order No / Ref" }),
270
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Date" }),
271
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Customer" }),
272
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Amount" }),
273
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Status" })
274
+ ] }) }),
275
+ /* @__PURE__ */ jsxs(Table.Body, { children: [
276
+ payments.map((payment) => /* @__PURE__ */ jsxs(Table.Row, { children: [
277
+ /* @__PURE__ */ jsxs(Table.Cell, { children: [
278
+ "#",
279
+ payment.order_number
280
+ ] }),
281
+ /* @__PURE__ */ jsx(Table.Cell, { children: new Date(payment.date).toLocaleString() }),
282
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
283
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: payment.customer_name }),
284
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: payment.customer_email })
285
+ ] }) }),
286
+ /* @__PURE__ */ jsx(Table.Cell, { children: new Intl.NumberFormat("en-US", {
287
+ style: "currency",
288
+ currency: payment.currency_code
289
+ }).format(payment.amount) }),
290
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(StatusBadge, { color: payment.status === "captured" ? "green" : payment.status === "pending" ? "orange" : "red", children: payment.status.charAt(0).toUpperCase() + payment.status.slice(1) }) })
291
+ ] }, payment.id)),
292
+ payments.length === 0 && !loading && /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { colSpan: 5, className: "text-center py-8 text-ui-fg-subtle", children: "No Paystack payments found." }) })
293
+ ] })
294
+ ] }),
295
+ hasMore && !search && /* @__PURE__ */ jsx("div", { ref: lastElementRef, className: "p-4 text-center text-ui-fg-subtle", children: loadingMore ? "Loading more payments..." : "Scroll for more" })
296
+ ] })
297
+ ] });
298
+ }
299
+ const i18nTranslations0 = {};
300
+ const widgetModule = { widgets: [
301
+ {
302
+ Component: ManualPaymentWidget,
303
+ zone: ["order.details.after"]
304
+ },
305
+ {
306
+ Component: PaymentHistoryWidget,
307
+ zone: ["order.details.after"]
308
+ }
309
+ ] };
310
+ const routeModule = {
311
+ routes: [
312
+ {
313
+ Component: PaystackDashboard,
314
+ path: "/payments/paystack"
315
+ }
316
+ ]
317
+ };
318
+ const menuItemModule = {
319
+ menuItems: [
320
+ {
321
+ label: config.label,
322
+ icon: config.icon,
323
+ path: "/payments/paystack",
324
+ nested: void 0,
325
+ rank: void 0,
326
+ translationNs: void 0
327
+ }
328
+ ]
329
+ };
330
+ const formModule = { customFields: {} };
331
+ const displayModule = {
332
+ displays: {}
333
+ };
334
+ const i18nModule = { resources: i18nTranslations0 };
335
+ const plugin = {
336
+ widgetModule,
337
+ routeModule,
338
+ menuItemModule,
339
+ formModule,
340
+ displayModule,
341
+ i18nModule
342
+ };
343
+ export {
344
+ plugin as default
345
+ };
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ async function POST(req, res) {
6
+ const { id: order_id } = req.params;
7
+ const { amount, reference, note } = req.body;
8
+ const orderModule = req.scope.resolve(utils_1.Modules.ORDER);
9
+ const paymentModule = req.scope.resolve(utils_1.Modules.PAYMENT);
10
+ try {
11
+ const query = req.scope.resolve("query");
12
+ const { data: orders } = await query.graph({
13
+ entity: "order",
14
+ fields: ["id", "currency_code", "email", "total", "payment_collections.*", "payment_collections.payments.*"],
15
+ filters: { id: order_id }
16
+ });
17
+ const order = orders[0];
18
+ const paymentCollection = order.payment_collections?.[0];
19
+ if (!paymentCollection) {
20
+ return res.status(400).json({ message: "No payment collection found for this order" });
21
+ }
22
+ // 1. Create a manual payment session for the partial amount
23
+ const paymentSession = await paymentModule.createPaymentSession(paymentCollection.id, {
24
+ provider_id: "pp_system_default", // Medusa's default manual payment provider in v2
25
+ amount: amount,
26
+ currency_code: order.currency_code,
27
+ data: {
28
+ manual: true,
29
+ reference,
30
+ note,
31
+ },
32
+ });
33
+ // 2. Authorize the payment session
34
+ const authorizedPayment = await paymentModule.authorizePaymentSession(paymentSession.id, {});
35
+ // 3. Capture the payment to mark it as paid
36
+ const capturedPayment = await paymentModule.capturePayment({
37
+ payment_id: authorizedPayment.id,
38
+ amount: amount,
39
+ });
40
+ res.status(200).json({
41
+ message: "Manual payment recorded successfully",
42
+ payment: capturedPayment
43
+ });
44
+ }
45
+ catch (error) {
46
+ res.status(500).json({ message: error.message });
47
+ }
48
+ }
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL29yZGVycy9baWRdL21hbnVhbC1wYXltZW50L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBR0Esb0JBdURDO0FBekRELHFEQUFvRDtBQUU3QyxLQUFLLFVBQVUsSUFBSSxDQUFDLEdBQWtCLEVBQUUsR0FBbUI7SUFDaEUsTUFBTSxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDO0lBQ3BDLE1BQU0sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQyxJQUl2QyxDQUFDO0lBRUYsTUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JELE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUV6RCxJQUFJLENBQUM7UUFDSCxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN6QyxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztZQUN6QyxNQUFNLEVBQUUsT0FBTztZQUNmLE1BQU0sRUFBQyxDQUFDLElBQUksRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxnQ0FBZ0MsQ0FBQztZQUMzRyxPQUFPLEVBQUUsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFO1NBQzFCLENBQUMsQ0FBQztRQUNILE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4QixNQUFNLGlCQUFpQixHQUFHLEtBQUssQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pELElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1lBQ3ZCLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsNENBQTRDLEVBQUUsQ0FBQyxDQUFDO1FBQ3pGLENBQUM7UUFFRCw0REFBNEQ7UUFDNUQsTUFBTSxjQUFjLEdBQUcsTUFBTSxhQUFhLENBQUMsb0JBQW9CLENBQUMsaUJBQWlCLENBQUMsRUFBRSxFQUFFO1lBQ3BGLFdBQVcsRUFBRSxtQkFBbUIsRUFBRSxpREFBaUQ7WUFDbkYsTUFBTSxFQUFFLE1BQU07WUFDZCxhQUFhLEVBQUUsS0FBSyxDQUFDLGFBQWE7WUFDbEMsSUFBSSxFQUFFO2dCQUNKLE1BQU0sRUFBRSxJQUFJO2dCQUNaLFNBQVM7Z0JBQ1QsSUFBSTthQUNMO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsbUNBQW1DO1FBQ25DLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxhQUFhLENBQUMsdUJBQXVCLENBQ25FLGNBQWMsQ0FBQyxFQUFFLEVBQ2pCLEVBQUUsQ0FDSCxDQUFDO1FBRUYsNENBQTRDO1FBQzVDLE1BQU0sZUFBZSxHQUFHLE1BQU0sYUFBYSxDQUFDLGNBQWMsQ0FBQztZQUN6RCxVQUFVLEVBQUUsaUJBQWlCLENBQUMsRUFBRTtZQUNoQyxNQUFNLEVBQUUsTUFBTTtTQUNmLENBQUMsQ0FBQztRQUVILEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO1lBQ25CLE9BQU8sRUFBRSxzQ0FBc0M7WUFDL0MsT0FBTyxFQUFFLGVBQWU7U0FDekIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7UUFDcEIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDbkQsQ0FBQztBQUNILENBQUMifQ==
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GET = GET;
7
+ const paystack_1 = __importDefault(require("../../../../lib/paystack"));
8
+ async function GET(req, res) {
9
+ try {
10
+ const paystackSecret = process.env.PAYSTACK_SECRET_KEY;
11
+ if (!paystackSecret) {
12
+ throw new Error("PAYSTACK_SECRET_KEY is not configured.");
13
+ }
14
+ const paystack = new paystack_1.default(paystackSecret);
15
+ const page = parseInt(req.query.page) || 1;
16
+ const search = req.query.search || "";
17
+ let transactions = [];
18
+ let currentBalances = {};
19
+ let totalsByCurrency = {};
20
+ let chartData = [];
21
+ if (search) {
22
+ // SEARCH MODE
23
+ try {
24
+ // Try to verify as a Paystack reference first
25
+ const verifyRes = await paystack.transaction.verify(search);
26
+ if (verifyRes.data) {
27
+ transactions = [verifyRes.data];
28
+ }
29
+ }
30
+ catch (e) {
31
+ // If it fails, try to search Medusa orders by display_id
32
+ if (!isNaN(Number(search))) {
33
+ const query = req.scope.resolve("query");
34
+ const { data: orders } = await query.graph({
35
+ entity: "order",
36
+ fields: ["payment_collections.payments.data"],
37
+ filters: { display_id: Number(search) }
38
+ });
39
+ if (orders.length > 0) {
40
+ // Extract reference
41
+ for (const order of orders) {
42
+ for (const pc of order.payment_collections || []) {
43
+ for (const payment of pc.payments || []) {
44
+ const ref = payment.data?.paystackTxRef || payment.data?.reference;
45
+ if (ref) {
46
+ try {
47
+ const vRes = await paystack.transaction.verify(ref);
48
+ if (vRes.data)
49
+ transactions.push(vRes.data);
50
+ }
51
+ catch (err) { }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ else {
61
+ // NORMAL / PAGINATION MODE
62
+ const response = await paystack.transaction.list({ perPage: 50, page });
63
+ transactions = response.data || [];
64
+ if (page === 1) {
65
+ const [balanceResponse, totalsResponse] = await Promise.all([
66
+ paystack.transaction.balance(),
67
+ paystack.transaction.totals()
68
+ ]);
69
+ const balancesData = balanceResponse.data || [];
70
+ for (const b of balancesData) {
71
+ currentBalances[b.currency] = b.balance / 100;
72
+ }
73
+ const totalsData = totalsResponse.data?.total_volume_by_currency || [];
74
+ for (const t of totalsData) {
75
+ totalsByCurrency[t.currency] = t.amount / 100;
76
+ }
77
+ // Calculate chart data from the first 50 transactions
78
+ const monthlyDataMap = {};
79
+ for (const tx of transactions) {
80
+ if (tx.status === "success") {
81
+ const amount = tx.amount / 100;
82
+ const currency = (tx.currency || "NGN").toUpperCase();
83
+ const date = new Date(tx.created_at);
84
+ const monthYear = `${date.toLocaleString('default', { month: 'short' })} ${date.getFullYear()}`;
85
+ if (!monthlyDataMap[monthYear])
86
+ monthlyDataMap[monthYear] = {};
87
+ monthlyDataMap[monthYear][currency] = (monthlyDataMap[monthYear][currency] || 0) + amount;
88
+ }
89
+ }
90
+ chartData = Object.entries(monthlyDataMap).map(([name, currencies]) => ({
91
+ name,
92
+ ...currencies
93
+ })).reverse();
94
+ }
95
+ }
96
+ const paymentsList = [];
97
+ for (const tx of transactions) {
98
+ const amount = tx.amount / 100;
99
+ const currency = (tx.currency || "NGN").toUpperCase();
100
+ const customerName = tx.customer?.first_name
101
+ ? `${tx.customer.first_name} ${tx.customer.last_name || ""}`.trim()
102
+ : "Guest";
103
+ const orderNumber = tx.metadata?.order_id || tx.reference;
104
+ let uiStatus = "pending";
105
+ if (tx.status === "success")
106
+ uiStatus = "captured";
107
+ if (tx.status === "failed" || tx.status === "abandoned" || tx.status === "reversed")
108
+ uiStatus = "canceled";
109
+ paymentsList.push({
110
+ id: tx.id,
111
+ order_number: orderNumber,
112
+ date: tx.created_at,
113
+ customer_name: customerName,
114
+ customer_email: tx.customer?.email || "N/A",
115
+ amount: amount,
116
+ currency_code: currency,
117
+ status: uiStatus
118
+ });
119
+ }
120
+ res.json({
121
+ totals: totalsByCurrency,
122
+ balances: currentBalances,
123
+ chart_data: chartData,
124
+ payments: paymentsList,
125
+ has_more: transactions.length === 50
126
+ });
127
+ }
128
+ catch (error) {
129
+ res.status(500).json({ message: error.message });
130
+ }
131
+ }
132
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3BheXN0YWNrL2Rhc2hib2FyZC9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUdBLGtCQStIQztBQWpJRCx3RUFBZ0Q7QUFFekMsS0FBSyxVQUFVLEdBQUcsQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQy9ELElBQUksQ0FBQztRQUNILE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQTZCLENBQUM7UUFDakUsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3BCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0NBQXdDLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxrQkFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzlDLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNyRCxNQUFNLE1BQU0sR0FBSSxHQUFHLENBQUMsS0FBSyxDQUFDLE1BQWlCLElBQUksRUFBRSxDQUFDO1FBRWxELElBQUksWUFBWSxHQUFVLEVBQUUsQ0FBQztRQUM3QixJQUFJLGVBQWUsR0FBMkIsRUFBRSxDQUFDO1FBQ2pELElBQUksZ0JBQWdCLEdBQTJCLEVBQUUsQ0FBQztRQUNsRCxJQUFJLFNBQVMsR0FBVSxFQUFFLENBQUM7UUFFMUIsSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUNYLGNBQWM7WUFDZCxJQUFJLENBQUM7Z0JBQ0gsOENBQThDO2dCQUM5QyxNQUFNLFNBQVMsR0FBRyxNQUFNLFFBQVEsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUM1RCxJQUFJLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDbkIsWUFBWSxHQUFHLENBQUMsU0FBUyxDQUFDLElBQUks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
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = GET;
4
+ async function GET(req, res) {
5
+ res.sendStatus(200);
6
+ }
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQUtDO0FBTE0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ async function POST(req, res) {
6
+ const { id: order_id } = req.params;
7
+ const { amount, email, metadata, callback_url } = req.body;
8
+ const orderModule = req.scope.resolve(utils_1.Modules.ORDER);
9
+ const paymentModule = req.scope.resolve(utils_1.Modules.PAYMENT);
10
+ try {
11
+ // 1. Retrieve the order and its payment collections
12
+ const query = req.scope.resolve("query");
13
+ const { data: orders } = await query.graph({
14
+ entity: "order",
15
+ fields: ["id", "currency_code", "email", "total", "payment_collections.*", "payment_collections.payments.*"],
16
+ filters: { id: order_id }
17
+ });
18
+ const order = orders[0];
19
+ const paymentCollection = order.payment_collections?.[0];
20
+ // 2. Fallback to the order's email (Perfect for Guest Customers)
21
+ const customerEmail = email || order.email;
22
+ if (!customerEmail) {
23
+ return res.status(400).json({ message: "An email address is required to process Paystack payments." });
24
+ }
25
+ if (!paymentCollection) {
26
+ return res.status(400).json({ message: "No payment collection found for this order" });
27
+ }
28
+ // 3. Prevent Overpayment Calculation
29
+ const payments = paymentCollection.payments || [];
30
+ const capturedAmount = payments.reduce((acc, p) => acc + (p.captured_at ? Number(p.amount) : 0), 0);
31
+ const remainingBalance = Number(order.total) - capturedAmount;
32
+ if (amount > remainingBalance) {
33
+ return res.status(400).json({
34
+ message: `Cannot pay more than the remaining balance. Remaining: ${remainingBalance}, Requested: ${amount}`
35
+ });
36
+ }
37
+ // 4. Create a new payment session for the partial amount
38
+ const paymentSession = await paymentModule.createPaymentSession(paymentCollection.id, {
39
+ provider_id: "pp_paystack",
40
+ amount: amount,
41
+ currency_code: order.currency_code,
42
+ data: {
43
+ email: customerEmail, // Uses the guest's original order email
44
+ order_id: order.id,
45
+ is_partial: true,
46
+ callback_url,
47
+ ...metadata,
48
+ },
49
+ });
50
+ res.status(200).json({
51
+ message: "Payment session created successfully",
52
+ payment_session: paymentSession
53
+ });
54
+ }
55
+ catch (error) {
56
+ res.status(500).json({ message: error.message });
57
+ }
58
+ }
59
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL29yZGVycy9baWRdL3BheXN0YWNrLXBheW1lbnQvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFHQSxvQkFrRUM7QUFwRUQscURBQW9EO0FBRTdDLEtBQUssVUFBVSxJQUFJLENBQUMsR0FBa0IsRUFBRSxHQUFtQjtJQUNoRSxNQUFNLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUM7SUFDcEMsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxHQUFHLEdBQUcsQ0FBQyxJQUtyRCxDQUFDO0lBRUYsTUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JELE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUV6RCxJQUFJLENBQUM7UUFDSCxvREFBb0Q7UUFDcEQsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUM7WUFDekMsTUFBTSxFQUFFLE9BQU87WUFDZixNQUFNLEVBQUMsQ0FBQyxJQUFJLEVBQUUsZUFBZSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsZ0NBQWdDLENBQUM7WUFDM0csT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRTtTQUMxQixDQUFDLENBQUM7UUFDSCxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDeEIsTUFBTSxpQkFBaUIsR0FBRyxLQUFLLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUV6RCxpRUFBaUU7UUFDakUsTUFBTSxhQUFhLEdBQUcsS0FBSyxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUM7UUFFM0MsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ25CLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsNERBQTRELEVBQUUsQ0FBQyxDQUFDO1FBQ3pHLENBQUM7UUFFRCxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUN2QixPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLDRDQUE0QyxFQUFFLENBQUMsQ0FBQztRQUN6RixDQUFDO1FBRUQscUNBQXFDO1FBQ3JDLE1BQU0sUUFBUSxHQUFHLGlCQUFpQixDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7UUFDbEQsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3BHLE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxjQUFjLENBQUM7UUFFOUQsSUFBSSxNQUFNLEdBQUcsZ0JBQWdCLEVBQUUsQ0FBQztZQUM5QixPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO2dCQUMxQixPQUFPLEVBQUUsMERBQTBELGdCQUFnQixnQkFBZ0IsTUFBTSxFQUFFO2FBQzVHLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCx5REFBeUQ7UUFDekQsTUFBTSxjQUFjLEdBQUcsTUFBTSxhQUFhLENBQUMsb0JBQW9CLENBQUMsaUJBQWlCLENBQUMsRUFBRSxFQUFFO1lBQ3BGLFdBQVcsRUFBRSxhQUFhO1lBQzFCLE1BQU0sRUFBRSxNQUFNO1lBQ2QsYUFBYSxFQUFFLEtBQUssQ0FBQyxhQUFhO1lBQ2xDLElBQUksRUFBRTtnQkFDSixLQUFLLEVBQUUsYUFBYSxFQUFFLHdDQUF3QztnQkFDOUQsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFO2dCQUNsQixVQUFVLEVBQUUsSUFBSTtnQkFDaEIsWUFBWTtnQkFDWixHQUFHLFFBQVE7YUFDWjtTQUNGLENBQUMsQ0FBQztRQUVILEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO1lBQ25CLE9BQU8sRUFBRSxzQ0FBc0M7WUFDL0MsZUFBZSxFQUFFLGNBQWM7U0FDaEMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7UUFDcEIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDbkQsQ0FBQztBQUNILENBQUMifQ==
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = GET;
4
+ async function GET(req, res) {
5
+ res.sendStatus(200);
6
+ }
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQUtDO0FBTE0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9