UNPKG

@variablevic/mollie-payments-medusa

Version:

A Medusa plugin for Mollie payments.

463 lines 34 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("@medusajs/framework/utils"); const api_client_1 = __importStar(require("@mollie/api-client")); /** * Implementation of Mollie Payment Provider for Medusa */ class MollieBase extends utils_1.AbstractPaymentProvider { /** * Validates that the required options are provided * @param options - The options to validate * @throws {MedusaError} If API key is missing */ static validateOptions(options) { if (!options.apiKey || !options.redirectUrl || !options.medusaUrl) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "API key, redirect URL, and Medusa URL are required in the provider's options."); } } /** * Creates a new instance of the Mollie payment provider * @param container - The dependency container * @param options - Configuration options */ constructor(container, options) { super(container, options); this.logger_ = container.logger; this.options_ = options; this.debug_ = options.debug || process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test" || false; this.client_ = (0, api_client_1.default)({ apiKey: options.apiKey, versionStrings: [ "MedusaJS/" + require("@medusajs/medusa/package.json").version, "VariableVic/" + process.env.npm_package_version, ], }); } normalizePaymentCreateParams() { const res = {}; if (this.paymentCreateOptions.method) { res.method = this.paymentCreateOptions.method; } res.webhookUrl = this.paymentCreateOptions.webhookUrl; res.captureMode = this.paymentCreateOptions.captureMethod ?? (this.options_.autoCapture !== false ? api_client_1.CaptureMethod.automatic : api_client_1.CaptureMethod.manual); return res; } /** * Initiates a new payment with Mollie * @param input - The payment initiation input * @returns The initiated payment details */ async initiatePayment({ context, amount, currency_code, }) { const normalizedParams = this.normalizePaymentCreateParams(); try { const createParams = { ...normalizedParams, billingAddress: { streetAndNumber: context?.customer?.billing_address?.address_1 || "", postalCode: context?.customer?.billing_address?.postal_code || "", city: context?.customer?.billing_address?.city || "", country: context?.customer?.billing_address?.country_code || "", }, billingEmail: context?.customer?.email || "", amount: { value: parseFloat(amount.toString()).toFixed(2), currency: currency_code.toUpperCase(), }, description: this.options_.description || "Mollie payment created by Medusa", redirectUrl: this.options_.redirectUrl, metadata: { idempotency_key: context?.idempotency_key, }, }; const data = await this.client_.payments .create(createParams) .then((payment) => { return payment; }) .catch((error) => { this.logger_.error(`Mollie payment creation failed: ${error.message}`); throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, error.message); }); this.debug_ && this.logger_.info(`Mollie payment ${data.id} successfully created with amount ${amount}`); return { id: data.id, data: data, }; } catch (error) { this.logger_.error(`Error initiating Mollie payment: ${error.message}`); throw error; } } /** * Checks if a payment is authorized with Mollie * @param input - The payment authorization input * @returns The authorization result */ async authorizePayment(input) { const externalId = input.data?.id; if (!externalId) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Payment ID is required"); } try { const { status } = await this.getPaymentStatus({ data: { id: externalId, }, }); if (!["captured", "authorized", "paid"].includes(status)) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Payment is not authorized: current status is ${status}`); } this.debug_ && this.logger_.info(`Mollie payment ${externalId} successfully authorized with status ${status}`); return { data: input.data, status, }; } catch (error) { this.logger_.error(`Error authorizing payment ${externalId}: ${error.message}`); throw error; } } /** * Captures an authorized payment if autoCapture is disabled * @param input - The payment capture input * @returns The capture result */ async capturePayment(input) { const externalId = input.data?.id; if (!externalId) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Payment ID is required"); } try { let status; const data = await this.retrievePayment({ data: { id: externalId, }, }).then(({ data }) => data); status = data?.status; const captureMode = data?.captureMode; if (status === api_client_1.PaymentStatus.authorized && captureMode === api_client_1.CaptureMethod.manual) { await this.client_.paymentCaptures.create({ paymentId: externalId, }); } status = await this.getPaymentStatus({ data: { id: externalId, }, }).then((res) => res.status); if (status !== utils_1.PaymentSessionStatus.CAPTURED) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Payment is not captured: current status is ${status}`); } this.debug_ && this.logger_.info(`Mollie payment ${externalId} captured with amount ${(input.data?.amount).currency_code} ${(input.data?.amount).value}`); const payment = await this.retrievePayment({ data: { id: externalId, }, }); return { data: payment.data, }; } catch (error) { this.logger_.error(`Error capturing payment ${externalId}: ${error.message}`); throw error; } } /** * Refunds a payment * @param input - The payment refund input * @returns The refund result */ async refundPayment(input) { const externalId = input.data?.id; if (!externalId) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Payment ID is required"); } try { const payment = await this.retrievePayment({ data: { id: externalId, }, }); const value = (input.data?.amount).value; const currency = payment.data?.amount ?.currency; if (!currency) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Currency information is missing from payment data"); } const refund = await this.client_.paymentRefunds.create({ paymentId: externalId, amount: { value: parseFloat(value.toString()).toFixed(2), currency: currency.toUpperCase(), }, }); this.debug_ && this.logger_.info(`Refund for Mollie payment ${externalId} created with amount ${currency.toUpperCase()} ${parseFloat(value.toString()).toFixed(2)}`); return { data: { ...refund }, }; } catch (error) { this.logger_.error(`Error refunding payment ${externalId}: ${error.message}`); throw error; } } /** * Cancels a payment * @param input - The payment cancellation input * @returns The cancellation result */ async cancelPayment(input) { const { id } = input.data; try { const payment = await this.client_.payments.get(id); if (payment.status === api_client_1.PaymentStatus.expired) { this.debug_ && this.logger_.info(`Mollie payment ${id} is already expired, no need to cancel`); return { data: { id: input.data?.id, }, }; } const newPayment = await this.client_.payments .cancel(id) .catch((error) => { this.logger_.warn(`Could not cancel Mollie payment ${id}: ${error.message}`); return { data: payment }; }); this.debug_ && this.logger_.info(`Mollie payment ${id} cancelled successfully`); return { data: newPayment, }; } catch (error) { this.logger_.error(`Error cancelling payment ${id}: ${error.message}`); throw error; } } /** * Deletes a payment (equivalent to cancellation as Mollie does not support deletion) * @param input - The payment deletion input * @returns The deletion result */ async deletePayment(input) { return this.cancelPayment(input); } /** * Gets the status of a payment by mapping Mollie statuses to Medusa statuses * @param input - The payment status input * @returns The payment status */ async getPaymentStatus(input) { const paymentId = input.data?.id; try { const { status } = await this.client_.payments.get(paymentId); const statusMap = { [api_client_1.PaymentStatus.open]: utils_1.PaymentSessionStatus.REQUIRES_MORE, [api_client_1.PaymentStatus.canceled]: utils_1.PaymentSessionStatus.CANCELED, [api_client_1.PaymentStatus.pending]: utils_1.PaymentSessionStatus.PENDING, [api_client_1.PaymentStatus.authorized]: utils_1.PaymentSessionStatus.AUTHORIZED, [api_client_1.PaymentStatus.expired]: utils_1.PaymentSessionStatus.ERROR, [api_client_1.PaymentStatus.failed]: utils_1.PaymentSessionStatus.ERROR, [api_client_1.PaymentStatus.paid]: utils_1.PaymentSessionStatus.CAPTURED, }; const mappedStatus = statusMap[status]; this.debug_ && this.logger_.debug(`Mollie payment ${paymentId} status: ${status} (mapped to: ${mappedStatus})`); return { status: mappedStatus, }; } catch (error) { this.logger_.error(`Error retrieving payment status for ${paymentId}: ${error.message}`); throw error; } } /** * Retrieves payment details * @param input - The payment retrieval input * @returns The payment details */ async retrievePayment(input) { const paymentId = input.data?.id; try { const data = await this.client_.payments.get(paymentId); return { data: data, }; } catch (error) { this.logger_.error(`Error retrieving Mollie payment ${paymentId}: ${error.message}`); throw error; } } /** * Updates a payment * @param input - The payment update input * @returns The updated payment details */ async updatePayment(input) { this.debug_ && this.logger_.info("Note: Mollie does not allow updating amounts on an existing payment. \n" + "Check https://docs.mollie.com/reference/update-payment for allowed updates."); const { id, description, redirectUrl, cancelUrl, webhookUrl, metadata, restrictPaymentMethodsToCountry, } = input.data; try { const data = await this.client_.payments.update(id, { description, redirectUrl, cancelUrl, webhookUrl, metadata, restrictPaymentMethodsToCountry, }); this.debug_ && this.logger_.info(`Mollie payment ${id} successfully updated`); return { data: data, }; } catch (error) { this.logger_.error(`Error updating Mollie payment ${id}: ${error.message}`); throw error; } } /** * Processes webhook data from Mollie * @param payload - The webhook payload * @returns The action and data to be processed */ async getWebhookActionAndData(payload) { const { data } = payload; try { const { data: payment } = await this.retrievePayment({ data: { id: data.id, }, }).catch((e) => { throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, e.message); }); if (!payment) { throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Payment not found"); } const status = payment?.status; const session_id = payment?.metadata ?.idempotency_key; const amount = new utils_1.BigNumber(payment?.amount); const baseData = { amount, session_id, ...payment, }; switch (status) { case api_client_1.PaymentStatus.authorized: return { action: utils_1.PaymentActions.AUTHORIZED, data: baseData, }; case api_client_1.PaymentStatus.paid: return { action: utils_1.PaymentActions.SUCCESSFUL, data: baseData, }; case api_client_1.PaymentStatus.expired: case api_client_1.PaymentStatus.failed: return { action: utils_1.PaymentActions.FAILED, data: baseData, }; case api_client_1.PaymentStatus.canceled: return { action: utils_1.PaymentActions.CANCELED, data: baseData, }; case api_client_1.PaymentStatus.pending: return { action: utils_1.PaymentActions.PENDING, data: baseData, }; case api_client_1.PaymentStatus.open: return { action: utils_1.PaymentActions.REQUIRES_MORE, data: baseData, }; default: return { action: utils_1.PaymentActions.NOT_SUPPORTED, data: baseData, }; } } catch (error) { this.logger_.error(`Error processing webhook for payment ${data.id}: ${error.message}`); // Even with errors, try to construct a valid response if we have the payment const { data: payment } = await this.retrievePayment({ data: { id: data.id }, }).catch(() => ({ data: null })); if (payment) { return { action: "failed", data: { session_id: payment?.metadata?.session_id, amount: new utils_1.BigNumber(payment?.amount), ...payment, }, }; } throw error; } } } exports.default = MollieBase; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9sbGllLWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL21vbGxpZS9jb3JlL21vbGxpZS1iYXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBS0EscURBTW1DO0FBc0JuQyxpRUFLNEI7QUFZNUI7O0dBRUc7QUFDSCxNQUFlLFVBQVcsU0FBUSwrQkFBdUI7SUFNdkQ7Ozs7T0FJRztJQUNILE1BQU0sQ0FBQyxlQUFlLENBQUMsT0FBd0I7UUFDN0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2xFLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLCtFQUErRSxDQUNoRixDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsWUFBWSxTQUErQixFQUFFLE9BQXdCO1FBQ25FLEtBQUssQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxNQUFNO1lBQ1QsT0FBTyxDQUFDLEtBQUs7Z0JBQ2IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEtBQUssYUFBYTtnQkFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEtBQUssTUFBTTtnQkFDL0IsS0FBSyxDQUFDO1FBRVIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFBLG9CQUFrQixFQUFDO1lBQ2hDLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtZQUN0QixjQUFjLEVBQUU7Z0JBQ2QsV0FBVyxHQUFHLE9BQU8sQ0FB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