Skip to content

Commit

Permalink
feat(dashboard,core-flows,js-sdk,types,link-modules,payment): ability…
Browse files Browse the repository at this point in the history
… to copy payment link (#8630)

what: 

- enables a button to create a payment link when a payment delta is present
- api to delete order payment collection
- adds a pending amount to payment collections

Note: Not the happiest with the decision on when to create a payment collection and when not to. The code should programatically create or delete payment collections currently to generate the right collection for the payment delta. Adding a more specific flow to create and manage a payment collection will help reduce this burden from the code path and onto CX/merchant.

Another issue I found is that the payment collection status doesn't get updated when payment is complete as it still gets stuck to "authorized" state

https://github.com/user-attachments/assets/037a10f9-3621-43c2-94ba-1ada4b0a041b
  • Loading branch information
riqwan authored Aug 20, 2024
1 parent 69830ca commit fa44e3f
Show file tree
Hide file tree
Showing 35 changed files with 630 additions and 93 deletions.
15 changes: 14 additions & 1 deletion integration-tests/http/__tests__/claims/claims.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ medusaIntegrationTestRunner({
})

it("should create a payment collection successfully and throw on multiple", async () => {
const paymentDelta = 110.5
const paymentDelta = 171.5

const paymentCollection = (
await api.post(
Expand Down Expand Up @@ -752,6 +752,19 @@ medusaIntegrationTestRunner({
message:
"Active payment collections were found. Complete existing ones or delete them before proceeding.",
})

const deleted = (
await api.delete(
`/admin/payment-collections/${paymentCollection.id}`,
adminHeaders
)
).data

expect(deleted).toEqual({
id: expect.any(String),
object: "payment-collection",
deleted: true,
})
})
})

Expand Down
1 change: 1 addition & 0 deletions packages/admin-next/dashboard/src/hooks/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from "./inventory"
export * from "./invites"
export * from "./notification"
export * from "./orders"
export * from "./payment-collections"
export * from "./payments"
export * from "./price-lists"
export * from "./product-types"
Expand Down
24 changes: 18 additions & 6 deletions packages/admin-next/dashboard/src/hooks/api/orders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export const useCreateOrderFulfillment = (
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})

queryClient.invalidateQueries({
queryKey: ordersQueryKeys.preview(orderId),
})

options?.onSuccess?.(data, variables, context)
},
...options,
Expand All @@ -107,6 +112,11 @@ export const useCancelOrderFulfillment = (
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})

queryClient.invalidateQueries({
queryKey: ordersQueryKeys.preview(orderId),
})

options?.onSuccess?.(data, variables, context)
},
...options,
Expand All @@ -129,25 +139,27 @@ export const useCreateOrderShipment = (
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})

queryClient.invalidateQueries({
queryKey: ordersQueryKeys.preview(orderId),
})

options?.onSuccess?.(data, variables, context)
},
...options,
})
}

export const useCancelOrder = (
orderId: string,
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: () => sdk.admin.order.cancel(orderId),
mutationFn: (id) => sdk.admin.order.cancel(id),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.lists(),
queryKey: ordersQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
},
...options,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { FetchError } from "@medusajs/js-sdk"
import { HttpTypes } from "@medusajs/types"
import { useMutation, UseMutationOptions } from "@tanstack/react-query"
import { sdk } from "../../lib/client"
import { queryClient } from "../../lib/query-client"
import { queryKeysFactory } from "../../lib/query-key-factory"
import { ordersQueryKeys } from "./orders"

const PAYMENT_COLLECTION_QUERY_KEY = "payment-collection" as const
export const paymentCollectionQueryKeys = queryKeysFactory(
PAYMENT_COLLECTION_QUERY_KEY
)

export const useCreatePaymentCollection = (
options?: UseMutationOptions<
HttpTypes.AdminPaymentCollectionResponse,
Error,
HttpTypes.AdminCreatePaymentCollection
>
) => {
return useMutation({
mutationFn: (payload) => sdk.admin.paymentCollection.create(payload),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.all,
})

queryClient.invalidateQueries({
queryKey: paymentCollectionQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
},
...options,
})
}

export const useDeletePaymentCollection = (
options?: Omit<
UseMutationOptions<
HttpTypes.AdminDeletePaymentCollectionResponse,
FetchError,
string
>,
"mutationFn"
>
) => {
return useMutation({
mutationFn: (id: string) => sdk.admin.paymentCollection.delete(id),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.all,
})

queryClient.invalidateQueries({
queryKey: paymentCollectionQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
},
...options,
})
}
12 changes: 2 additions & 10 deletions packages/admin-next/dashboard/src/hooks/api/payments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ export const useCapturePayment = (
mutationFn: (payload) => sdk.admin.payment.capture(paymentId, payload),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})

queryClient.invalidateQueries({
queryKey: ordersQueryKeys.lists(),
queryKey: ordersQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
Expand All @@ -95,11 +91,7 @@ export const useRefundPayment = (
mutationFn: (payload) => sdk.admin.payment.refund(paymentId, payload),
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: ordersQueryKeys.details(),
})

queryClient.invalidateQueries({
queryKey: ordersQueryKeys.lists(),
queryKey: ordersQueryKeys.all,
})

options?.onSuccess?.(data, variables, context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { sdk } from "../../lib/client"
import { queryKeysFactory } from "../../lib/query-key-factory"

const REFUND_REASON_QUERY_KEY = "refund-reason" as const
export const paymentQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY)
export const refundReasonQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY)

export const useRefundReasons = (
query?: HttpTypes.RefundReasonFilters,
Expand Down
4 changes: 3 additions & 1 deletion packages/admin-next/dashboard/src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,9 @@
"capturePaymentSuccess": "Payment of {{amount}} successfully captured",
"createRefund": "Create Refund",
"refundPaymentSuccess": "Refund of amount {{amount}} successful",
"createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}"
"createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}",
"refundAmount": "Refund {{ amount }}",
"paymentLink": "Copy payment link for {{ amount }}"
},

"edits": {
Expand Down
9 changes: 9 additions & 0 deletions packages/admin-next/dashboard/src/lib/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ export const getTotalCaptured = (
(paymentCollection.refunded_amount as number))
return acc
}, 0)

export const getTotalPending = (paymentCollections: AdminPaymentCollection[]) =>
paymentCollections.reduce((acc, paymentCollection) => {
acc +=
(paymentCollection.amount as number) -
(paymentCollection.captured_amount as number)

return acc
}, 0)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AdminOrder } from "@medusajs/types"
import { Alert, Button, Select, Switch, toast } from "@medusajs/ui"
import { useForm, useWatch } from "react-hook-form"

import { OrderLineItemDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form"
import {
RouteFocusModal,
Expand Down Expand Up @@ -118,8 +119,18 @@ export function OrderCreateFulfillmentForm({

if (itemsToFulfill?.length) {
setFulfillableItems(itemsToFulfill)

const quantityMap = fulfillableItems.reduce(
(acc, item) => {
acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO)
return acc
},
{} as Record<string, number>
)

form.setValue("quantity", quantityMap)
}
}, [order.items])
}, [order.items?.length])

return (
<RouteFocusModal.Form form={form}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/modals"
import { CreateShipmentSchema } from "./constants"
import { useCreateOrderShipment } from "../../../../../hooks/api"
import { CreateShipmentSchema } from "./constants"

type OrderCreateFulfillmentFormProps = {
order: AdminOrder
Expand All @@ -27,7 +27,7 @@ export function OrderCreateShipmentForm({
const { handleSuccess } = useRouteModal()

const { mutateAsync: createShipment, isPending: isMutating } =
useCreateOrderShipment(order.id, fulfillment.id)
useCreateOrderShipment(order.id, fulfillment?.id)

const form = useForm<zod.infer<typeof CreateShipmentSchema>>({
defaultValues: {
Expand All @@ -44,7 +44,7 @@ export function OrderCreateShipmentForm({
const handleSubmit = form.handleSubmit(async (data) => {
await createShipment(
{
items: fulfillment.items.map((i) => ({
items: fulfillment?.items?.map((i) => ({
id: i.line_item_id,
quantity: i.quantity,
})),
Expand Down
Loading

0 comments on commit fa44e3f

Please sign in to comment.