0.2.1 • Published 7 months ago

medusajs-payment-mollie v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

medusajs-payment-mollie

Note: This package is currently in development. We encourage you to test the package in your environment and report any issues or improvements.

Table of Contents


Installation

To install the medusajs-payment-mollie package, you can use either npm or yarn:

npm install medusajs-payment-mollie
yarn add medusajs-payment-mollie

Configuration

Backend Configuration

Add Mollie as a Payment Provider

In your Medusa server, update the medusa-config.js file to include Mollie as a payment provider:

modules: [
  {
    resolve: "@medusajs/medusa/payment",
    options: {
      providers: [
        {
          resolve: "medusajs-payment-mollie",
          id: "mollie",
          options: {
            /**
             * The ID assigned to the payment provider in `medusa-config.ts`.
             * This ID will be used to construct the webhook URL for receiving events from the Mollie API.
             */
            providerId: "mollie",
            // Your Mollie API key (use either live or test key)
            apiKey: process.env.MOLLIE_API_KEY,
            // Default description for payments when not provided
            paymentDescription: "mollie payment default description",
            /**
             * The base URL for the webhook. This will be used to construct the complete webhook URL for Mollie events.
             * For example:
             *   webhookUrl: `https://example.com`
             *   providerId: `mollie`
             * The final callback URL will be: `https://example.com/hooks/payment/mollie_mollie`
             *
             * The `webhookUrl` should always point to the domain where the Medusa backend is deployed.
             */
            webhookUrl: "https://your-domain.com",
          },
        },
      ],
    },
  },
];

Create a Custom API Endpoint

Add a custom API endpoint to list available Mollie payment methods:

src/api/store/mollie/payment-methods/route.ts

import MollieClient from "@mollie/api-client";
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";

export async function GET(req: MedusaRequest, res: MedusaResponse) {
  const mollieClient = MollieClient({
    apiKey: process.env.MOLLIE_API_KEY || "",
  });

  const methods = await mollieClient.methods.list();

  res.status(200).json(methods);
}

Frontend Configuration

Handle the Place Order Logic

To complete the order after a successful Mollie transaction, create an API route for order completion and redirection:

/src/app/api/place-order/[cartId]/route.ts

import { sdk } from "@lib/config";
import { revalidateTag } from "next/cache";
import medusaError from "@lib/util/medusa-error";
import { getAuthHeaders, removeCartId } from "@lib/data/cookies";

type Params = { params: Promise<{ cartId: string }> };

export async function GET(_: Request, { params }: Params) {
  try {
    const cartId = (await params).cartId;

    const cartRes = await sdk.store.cart
      .complete(cartId, {}, getAuthHeaders())
      .then((cartRes) => {
        revalidateTag("cart");
        return cartRes;
      })
      .catch(medusaError);

    if (cartRes?.type === "order") {
      const countryCode =
        cartRes.order.shipping_address?.country_code?.toLowerCase();
      removeCartId();

      return Response.redirect(
        new URL(
          `/${countryCode}/order/confirmed/${cartRes?.order.id}`,
          process.env.NEXT_PUBLIC_BASE_URL
        )
      );
    }
    return Response.redirect(new URL("/", process.env.NEXT_PUBLIC_BASE_URL));
  } catch (error: any) {
    return Response.json({ error: error?.message });
  }
}

Update initiatePaymentSession method

In your frontend component, update the initiatePaymentSession method to include Mollie. This ensures the payment session is correctly created.

/src/modules/checkout/components/payment.tsx

await initiatePaymentSession(cart, {
  provider_id: providerId,
  context: {
    extra: {
      // selected payment method or null
      method,
      // url to which the customer should be redirected after transaction is complete
      redirectUrl,
      /// description for the payment
      paymentDescription: "new payment",
    },
  },
});
// import isMollie
import { isMollie as isMollieFunc } from "@lib/constants";

// handleSubmit
const handleSubmit = async () => {
  setError("");
  if (!cart || !selectedPaymentMethod) {
    return;
  }

  setIsLoading(true);
  try {
    const shouldInputCard =
      isStripeFunc(selectedPaymentMethod) && !activeSession;

    if (!activeSession) {
      let method: string | undefined = undefined;
      let providerId = selectedPaymentMethod;
      let redirectUrl: string | null = null;

      if (isMollieFunc(selectedPaymentMethod)) {
        const parts = selectedPaymentMethod.split("_");
        method = parts.pop();
        providerId = parts.join("_");
        redirectUrl = `${process.env.NEXT_PUBLIC_BASE_URL}/api/place-order/${cart.id}`;
      }

      await initiatePaymentSession(cart, {
        provider_id: providerId,
        context: {
          extra: {
            method,
            redirectUrl,
            paymentDescription: "new payment",
          },
        },
      });
    }

    if (!shouldInputCard) {
      return router.push(pathname + "?" + createQueryString("step", "review"), {
        scroll: false,
      });
    }
  } catch (err: any) {
    setError(err.message);
  } finally {
    setIsLoading(false);
  }
};

Mollie Payment Method Component

This component will render the Mollie payment options.

import { RadioGroup } from "@headlessui/react";
import { clx, Text } from "@medusajs/ui";
import Radio from "@modules/common/components/radio";
import { useEffect, useState } from "react";

const fetchMolliePaymentOptions = async () =>
  fetch(
    `${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/mollie/payment-methods`,
    {
      method: "GET",
      credentials: "include",
      headers: {
        "x-publishable-api-key":
          process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "",
      },
    }
  ).then((res) => res.json());

type PaymentOption = {
  description: string;
  id: string;
  image: {
    svg: string;
  };
};

const providerId = "pp_mollie_mollie";
export const MolliePaymentOptions = (props: {
  selectedOptionId: string;
  setSelectedOptionId: (value: string) => void;
}) => {
  const { selectedOptionId, setSelectedOptionId } = props;
  const [paymentOptions, setPaymentOptions] = useState<PaymentOption[]>([]);

  useEffect(() => {
    fetchMolliePaymentOptions()
      .then((methods) => {
        setPaymentOptions(methods);
      })
      .catch(console.log);
  }, []);

  return (
    <div>
      <RadioGroup
        value={selectedOptionId}
        onChange={(value: string) => setSelectedOptionId(value)}
      >
        {paymentOptions.map(({ description, id, image }) => (
          <RadioGroup.Option
            /// the prefix `pp_mollie_mollie_` should be same as the provider_id
            value={`pp_mollie_mollie_${id}`}
            key={id}
            className={clx(
              "flex flex-col gap-y-2 text-small-regular cursor-pointer py-4 border rounded-rounded px-8 mb-2 hover:shadow-borders-interactive-with-active",
              {
                "border-ui-border-interactive": selectedOptionId?.endsWith(id),
              }
            )}
          >
            <div className="flex items-center justify-between ">
              <div className="flex items-center gap-x-4">
                <Radio checked={selectedOptionId?.endsWith(id)} />
                <Text className="text-base-regular">{description}</Text>
              </div>
              <span className="justify-self-end text-ui-fg-base">
                <picture>
                  <img src={image.svg} alt={description} />
                </picture>
              </span>
            </div>
          </RadioGroup.Option>
        ))}
      </RadioGroup>
    </div>
  );
};

Contributing

To contribute:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feat/your-feature).
  3. Make your changes.
  4. Commit your changes (git commit -am 'Add new feature').
  5. Push to your branch (git push origin feat/your-feature).
  6. Create a new Pull Request.

If you encounter any issues, please open an issue on GitHub. If you have a fix, feel free to create a PR.

0.2.1

7 months ago

0.2.0

7 months ago

0.1.4

7 months ago

0.1.3

7 months ago

0.1.2

7 months ago

0.1.1

7 months ago

0.1.0

7 months ago

0.0.4

7 months ago