list payments
This commit is contained in:
parent
5172ab11e7
commit
c5f135fdcc
@ -67,6 +67,8 @@ export default async function webhook(req: BlitzApiRequest, res: BlitzApiRespons
|
||||
}
|
||||
|
||||
const alertName = req.body.alert_name;
|
||||
logger.info(`Received ${alertName} webhook`);
|
||||
logger.info(req.body);
|
||||
if (isSupportedWebhook(alertName)) {
|
||||
return handlers[alertName](req, res);
|
||||
}
|
||||
|
@ -1,28 +1,8 @@
|
||||
const payments = [
|
||||
{
|
||||
id: 1,
|
||||
date: new Date(),
|
||||
description: "",
|
||||
amount: "340 USD",
|
||||
href: "",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
date: new Date(),
|
||||
description: "",
|
||||
amount: "340 USD",
|
||||
href: "",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
date: new Date(),
|
||||
description: "",
|
||||
amount: "340 USD",
|
||||
href: "",
|
||||
},
|
||||
];
|
||||
import useSubscription from "../../hooks/use-subscription";
|
||||
|
||||
export default function BillingHistory() {
|
||||
const { payments } = useSubscription();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="bg-white pt-6 shadow sm:rounded-md sm:overflow-hidden">
|
||||
@ -46,17 +26,14 @@ export default function BillingHistory() {
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Description
|
||||
Amount
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Amount
|
||||
Status
|
||||
</th>
|
||||
{/*
|
||||
`relative` is added here due to a weird bug in Safari that causes `sr-only` headings to introduce overflow on the body on mobile.
|
||||
*/}
|
||||
<th
|
||||
scope="col"
|
||||
className="relative px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
@ -66,27 +43,33 @@ export default function BillingHistory() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{payments.map((payment) => (
|
||||
<tr key={payment.id}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<time>{payment.date.toDateString()}</time>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{payment.description}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{payment.amount}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a
|
||||
href={payment.href}
|
||||
className="text-primary-600 hover:text-primary-900"
|
||||
>
|
||||
View receipt
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{typeof payments !== "undefined"
|
||||
? payments.map((payment) => (
|
||||
<tr key={payment.id}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<time>{new Date(payment.payout_date).toDateString()}</time>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{payment.amount} {payment.currency}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{payment.is_paid === 1 ? "Paid" : "Not paid yet"}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
{typeof payment.receipt_url !== "undefined" ? (
|
||||
<a
|
||||
href={payment.receipt_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary-600 hover:text-primary-900"
|
||||
>
|
||||
View receipt
|
||||
</a>
|
||||
) : null}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@ import { useQuery, useMutation, useRouter, useSession } from "blitz";
|
||||
|
||||
import type { Subscription } from "db";
|
||||
import getSubscription from "../queries/get-subscription";
|
||||
import getPayments from "../queries/get-payments";
|
||||
import usePaddle from "./use-paddle";
|
||||
import useCurrentUser from "../../core/hooks/use-current-user";
|
||||
import updateSubscription from "../mutations/update-subscription";
|
||||
@ -14,7 +15,8 @@ type Params = {
|
||||
export default function useSubscription({ initialData }: Params = {}) {
|
||||
const session = useSession();
|
||||
const { user } = useCurrentUser();
|
||||
const [subscription] = useQuery(getSubscription, null, { enabled: Boolean(session.orgId), initialData });
|
||||
const [subscription] = useQuery(getSubscription, null, { initialData });
|
||||
const [payments] = useQuery(getPayments, null);
|
||||
const [updateSubscriptionMutation] = useMutation(updateSubscription);
|
||||
|
||||
const router = useRouter();
|
||||
@ -49,7 +51,7 @@ export default function useSubscription({ initialData }: Params = {}) {
|
||||
email: user.email,
|
||||
product: planId,
|
||||
allowQuantity: false,
|
||||
passthrough: JSON.stringify({ orgId: session.orgId }),
|
||||
passthrough: JSON.stringify({ organizationId: session.orgId }),
|
||||
coupon: "",
|
||||
};
|
||||
|
||||
@ -93,6 +95,7 @@ export default function useSubscription({ initialData }: Params = {}) {
|
||||
|
||||
return {
|
||||
subscription,
|
||||
payments,
|
||||
subscribe,
|
||||
updatePaymentMethod,
|
||||
cancelSubscription,
|
||||
|
20
app/settings/queries/get-payments.ts
Normal file
20
app/settings/queries/get-payments.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { resolver } from "blitz";
|
||||
|
||||
import db, { SubscriptionStatus } from "db";
|
||||
import { getPayments } from "integrations/paddle";
|
||||
|
||||
export default resolver.pipe(resolver.authorize(), async (_ = null, { session }) => {
|
||||
if (!session.orgId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const subscription = await db.subscription.findFirst({
|
||||
where: { organizationId: session.orgId, status: SubscriptionStatus.active },
|
||||
});
|
||||
if (!subscription) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const payments = await getPayments({ subscriptionId: subscription.paddleSubscriptionId });
|
||||
return payments.sort((a, b) => b.payout_date.localeCompare(a.payout_date));
|
||||
});
|
@ -1,9 +1,9 @@
|
||||
import type { Ctx } from "blitz";
|
||||
import { resolver } from "blitz";
|
||||
|
||||
import db from "db";
|
||||
|
||||
export default async function getCurrentUser(_ = null, { session }: Ctx) {
|
||||
export default resolver.pipe(resolver.authorize(), async (_ = null, { session }) => {
|
||||
if (!session.orgId) return null;
|
||||
|
||||
return db.subscription.findFirst({ where: { organizationId: session.orgId } });
|
||||
}
|
||||
});
|
||||
|
@ -12,12 +12,55 @@ const client = got.extend({
|
||||
|
||||
async function request<T>(path: string, data: any) {
|
||||
return client.post<T>(path, {
|
||||
...data,
|
||||
vendor_id,
|
||||
vendor_auth_code,
|
||||
json: {
|
||||
...data,
|
||||
vendor_id,
|
||||
vendor_auth_code,
|
||||
},
|
||||
responseType: "json",
|
||||
});
|
||||
}
|
||||
|
||||
type GetPaymentsParams = {
|
||||
subscriptionId: string;
|
||||
};
|
||||
|
||||
export async function getPayments({ subscriptionId }: GetPaymentsParams) {
|
||||
type Payment = {
|
||||
id: number;
|
||||
subscription_id: number;
|
||||
amount: number;
|
||||
currency: string;
|
||||
payout_date: string;
|
||||
is_paid: number;
|
||||
is_one_off_charge: boolean;
|
||||
receipt_url?: string;
|
||||
};
|
||||
|
||||
type PaymentsSuccessResponse = {
|
||||
success: true;
|
||||
response: Payment[];
|
||||
};
|
||||
|
||||
type PaymentsErrorResponse = {
|
||||
success: false;
|
||||
error: {
|
||||
code: number;
|
||||
message: string;
|
||||
};
|
||||
};
|
||||
|
||||
type PaymentsResponse = PaymentsSuccessResponse | PaymentsErrorResponse;
|
||||
|
||||
const { body } = await request<PaymentsResponse>("subscription/payments", { subscription_id: subscriptionId });
|
||||
console.log("body", typeof body);
|
||||
if (!body.success) {
|
||||
throw new Error(body.error.message);
|
||||
}
|
||||
|
||||
return body.response;
|
||||
}
|
||||
|
||||
type UpdateSubscriptionPlanParams = {
|
||||
subscriptionId: string;
|
||||
planId: string;
|
||||
@ -25,7 +68,7 @@ type UpdateSubscriptionPlanParams = {
|
||||
};
|
||||
|
||||
export async function updateSubscriptionPlan({ subscriptionId, planId, prorate = true }: UpdateSubscriptionPlanParams) {
|
||||
const { body } = await request("/subscription/users/update", {
|
||||
const { body } = await request("subscription/users/update", {
|
||||
subscription_id: subscriptionId,
|
||||
plan_id: planId,
|
||||
prorate,
|
||||
@ -35,7 +78,7 @@ export async function updateSubscriptionPlan({ subscriptionId, planId, prorate =
|
||||
}
|
||||
|
||||
export async function cancelPaddleSubscription({ subscriptionId }: { subscriptionId: string }) {
|
||||
const { body } = await request("/subscription/users_cancel", { subscription_id: subscriptionId });
|
||||
const { body } = await request("subscription/users_cancel", { subscription_id: subscriptionId });
|
||||
|
||||
return body;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user