UNPKG

@gmisoftware/przelewy24-payments-medusa

Version:

Przelewy24 (P24) payment integration plugin for Medusa e-commerce with BLIK, cards, and general P24 support.

498 lines 38.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = __importDefault(require("crypto")); const utils_1 = require("@medusajs/framework/utils"); const p24_api_1 = require("../services/p24-api"); class P24Base extends utils_1.AbstractPaymentProvider { static validateOptions(options) { if (!(0, utils_1.isDefined)(options.merchant_id)) { throw new Error("Required option `merchant_id` is missing in P24 plugin"); } if (!(0, utils_1.isDefined)(options.pos_id)) { throw new Error("Required option `pos_id` is missing in P24 plugin"); } if (!(0, utils_1.isDefined)(options.api_key)) { throw new Error("Required option `api_key` is missing in P24 plugin"); } if (!(0, utils_1.isDefined)(options.crc)) { throw new Error("Required option `crc` is missing in P24 plugin"); } if (!(0, utils_1.isDefined)(options.frontend_url)) { throw new Error("Required option `frontend_url` is missing in P24 plugin"); } if (!(0, utils_1.isDefined)(options.backend_url)) { throw new Error("Required option `backend_url` is missing in P24 plugin"); } } constructor(cradle, options) { // @ts-ignore super(...arguments); this.container_ = cradle; this.options_ = options; this.p24Api = new p24_api_1.P24ApiService(options); } get options() { return this.options_; } /** * Normalizes payment create parameters for P24 transaction registration. * * This method prepares the common parameters that will be used across all * P24 payment methods (cards, BLIK, etc.) based on the provider-specific options. * * @returns Normalized P24 transaction parameters */ normalizePaymentCreateParams() { const res = {}; res.description = this.paymentIntentOptions.description ?? "Payment via Przelewy24"; res.channel = this.paymentIntentOptions.channel; return res; } /** * Initiates a new payment with Przelewy24. * * This method creates a payment session with P24 by registering a transaction. * It uses provider-specific options (channel, method) from the concrete implementations * and normalizes common parameters for all P24 payment methods. * * @param input - The payment initiation input containing amount, currency, and context * @returns The initiated payment details with session ID and redirect URL */ async initiatePayment({ currency_code, amount, data, context, }) { try { console.log(`Initiating P24 payment - Amount: ${amount} ${currency_code}`); console.log(`Context:`, context); console.log("data: ", data); const normalizedParams = this.normalizePaymentCreateParams(); console.log("normalizedParams: ", normalizedParams); const customerEmail = context?.customer?.email || data?.customer?.email || "customer@example.com"; const country = data?.country?.toUpperCase() || "PL"; const language = data?.language?.toLowerCase() || "pl"; console.log(`Using country: ${country}, language: ${language}`); const transactionRequest = { sessionId: context?.idempotency_key, amount: Number(amount), country: country, language: language, currency: currency_code.toUpperCase(), description: normalizedParams.description || `Payment ${context?.idempotency_key}`, email: customerEmail, channel: normalizedParams.channel, urlReturn: `${this.options_.frontend_url}/payment/return?cart_id=${data?.cart_id}`, urlStatus: `${this.options_.backend_url}/hooks/payment/${this.getProviderKey()}_przelewy24`, }; console.log(`Registering P24 transaction with session ID: ${transactionRequest.sessionId}`); const sessionData = await this.p24Api.registerTransaction(transactionRequest); console.log(`P24 transaction response:`, sessionData); // Validate the response - check if P24 returned an error code if (sessionData.responseCode !== 0) { throw this.buildError("Failed to register P24 transaction", new Error(`P24 API error: ${sessionData.responseCode} - ${sessionData.message || "Unknown error"}`)); } console.log(`P24 payment ${transactionRequest.sessionId} successfully initiated`); return { id: transactionRequest.sessionId, data: { session_id: transactionRequest.sessionId, token: sessionData.data.token, redirect_url: `${this.p24Api.getBaseRedirectURL()}/${sessionData.data.token}`, amount: amount, currency: currency_code, description: transactionRequest.description, email: transactionRequest.email, country: country, language: language, channel: transactionRequest.channel, ...sessionData, }, }; } catch (error) { console.error(`Error initiating P24 payment: ${error.message}`); throw error; } } /** * Authorizes a payment session using the third-party payment provider. * * During checkout, the customer may need to perform actions required by the payment provider, * such as entering their card details or confirming the payment. Once that is done, the * customer can place their order. * * During cart-completion before placing the order, this method is used to authorize the * cart's payment session with the third-party payment provider. The payment can later be * captured using the capturePayment method. * * For P24, this method validates the payment status by fetching transaction details * from P24 and mapping P24 statuses (0-3) to Medusa payment statuses: * - 0: pending * - 1: authorized * - 2: captured * - 3: canceled * * @param input - The authorize payment input containing the payment data * @returns The payment data with authorization status */ async authorizePayment(input) { console.log("----- REACHED TO THE AUTHORIZE PAYMENT -----"); console.log("input from authorize: ", input); const sessionId = input.data?.session_id; if (!sessionId) { throw this.buildError("Session ID is required for payment authorization", new Error("No session ID provided")); } try { const { transactionDetails, p24Status, medusaStatus } = await this.getTransactionDetailsAndStatus(sessionId); console.log("transactionDetails (in authorize payment): ", transactionDetails); if (!["authorized", "captured", "pending"].includes(medusaStatus)) { throw this.buildError(`Payment is not in a valid state for authorization: current status is ${medusaStatus}`, new Error(`Invalid payment status: ${medusaStatus}`)); } return { data: { ...input.data, ...transactionDetails.data, p24_status: p24Status, }, status: medusaStatus, }; } catch (error) { console.error(`Error authorizing payment ${sessionId}: ${error.message}`); throw error; } } /** * Captures a payment using the third-party provider. * * This method provides manual payment capture capability for admin users. * In normal operation, payments are automatically verified and captured via webhook * processing in getWebhookActionAndData. This method serves as a backup option. * * According to P24 documentation, transaction/verify is required to confirm the payment * and transfer the amount to the merchant. Without verification, the amount stays as * an advance payment at the customer's disposal. * * @param input - The capture payment input containing the payment data * @returns The payment data with capture confirmation */ async capturePayment(input) { console.log("----- REACHED TO THE CAPTURE PAYMENT -----"); console.log("input from capture: ", input); const sessionId = input.data?.sessionId; const amount = input.data?.amount; const currency = input.data?.currency; const orderId = input.data?.orderId; if (!sessionId || !amount || !currency || !orderId) { throw this.buildError("Missing required data for payment capture", new Error("Session ID, amount, currency, and order ID are required")); } try { console.log(`Capturing payment ${sessionId} with order ID ${orderId}...`); // Call transaction/verify to confirm and capture the payment const verification = await this.p24Api.verifyTransaction(sessionId, amount, currency, orderId); if (verification.responseCode !== 0 || verification.data.status !== "success") { throw this.buildError("Payment capture failed", new Error(`Verification failed - responseCode: ${verification.responseCode}, status: ${verification.data.status}`)); } console.log(`Payment ${sessionId} successfully captured`); return { data: { ...input.data, status: "captured", captured_at: new Date().toISOString(), order_id: orderId, capture_verified: true, }, }; } catch (error) { console.error(`Error capturing payment ${sessionId}: ${error.message}`); throw error; } } /** * Deletes a payment session in the third-party payment provider. * * P24 doesn't support programmatic deletion of payment sessions. * Payment sessions expire automatically after 15 minutes. * * When a customer chooses a payment method during checkout, then chooses a different one, * this method is triggered to delete the previous payment session. * * @param input - The delete payment input containing the payment data * @returns The same payment data (no deletion performed) */ async deletePayment(input) { return { data: input.data, }; } /** * Cancels a payment in the third-party payment provider. * * P24 doesn't support programmatic cancellation of payments. * Payments can only be canceled through the P24 merchant panel. * * This method is used when the admin user cancels an order. * The order can only be canceled if the payment is not captured yet. * * @param input - The cancel payment input containing the payment data * @returns The same payment data (no cancellation performed) */ async cancelPayment(input) { return { data: input.data, }; } /** * Refunds an amount using the P24 payment provider. * * This method is triggered when the admin user refunds a payment of an order. * It creates a refund request to P24 using the transaction/refund endpoint. * * The method uses the session ID and order ID from the payment data to identify * the transaction to refund, and generates a unique UUID for tracking the refund. * * @param input - The input to refund the payment containing amount and payment data * @returns The payment data with refund information and status */ async refundPayment(input) { const { data: paymentData, amount: refundAmount, context } = input; const sessionId = paymentData?.session_id; const orderId = paymentData?.order_id; if (!sessionId || !orderId) { throw this.buildError("No session ID or order ID provided while refunding payment", new Error("Missing session ID or order ID")); } try { const currencyCode = paymentData?.currency || "PLN"; const refundsUuid = crypto_1.default.randomUUID().substring(0, 35); const requestId = context?.idempotency_key || `refund-${Date.now()}`; const refundData = { requestId: requestId, refunds: [ { orderId: orderId, sessionId: sessionId, amount: Number(refundAmount), description: `Refund for order ${sessionId}`, }, ], refundsUuid: refundsUuid, // urlStatus: this.options_.backend_url // ? `${ // this.options_.backend_url // }/hooks/refund/${this.getProviderKey()}_przelewy24` // : undefined, }; const refundResult = await this.p24Api.processRefund(refundData); // Check if refund was successful const refundStatus = refundResult.data[0]?.status; const refundMessage = refundResult.data[0]?.message; if (refundResult.responseCode !== 0 || !refundStatus) { throw this.buildError("Refund request failed", new Error(`P24 API error: ${refundResult.responseCode} - ${refundMessage || "Unknown error"}`)); } return { data: { ...paymentData, refund_amount: refundAmount, refunded_at: new Date().toISOString(), refunds_uuid: refundsUuid, refund_request_id: requestId, refund_status: refundStatus, refund_message: refundMessage, status: "refund_requested", }, }; } catch (e) { throw this.buildError("An error occurred in refundPayment", e); } } async retrievePayment(input) { try { const { data: paymentSessionData } = input; // Note: This would need the full transaction data to verify return { data: paymentSessionData, }; } catch (e) { throw this.buildError("An error occurred in retrievePayment", e); } } async updatePayment(input) { const { data, amount } = input; const amountNumeric = Number(amount); if (data?.amount === amountNumeric) { return { data }; } // P24 doesn't support updating payment amounts after creation // We need to create a new payment session throw this.buildError("P24 doesn't support updating payment amounts. Please create a new payment session.", new Error("Update not supported")); } /** * Processes webhook data from P24 * * @param webhookData - The webhook payload from P24 * @returns The action and data to be processed */ async getWebhookActionAndData(webhookData) { const { data } = webhookData; try { const payload = data; const { sessionId, orderId, amount, currency, sign } = payload; console.log("----------- P24 WEBHOOK RECEIVED -----------"); console.log("Session ID:", sessionId); console.log("Order ID:", orderId); console.log("Amount:", amount); console.log("Currency:", currency); if (!this.p24Api.verifyWebhookSignature(payload, sign)) { console.error(`Invalid webhook signature for session ${sessionId}`); return { action: utils_1.PaymentActions.NOT_SUPPORTED }; } console.log("Verifying transaction with P24..."); const verification = await this.p24Api.verifyTransaction(sessionId, amount, currency, orderId); if (verification.responseCode !== 0 || verification.data.status !== "success") { console.error(`Transaction verification failed - responseCode: ${verification.responseCode}, status: ${verification.data.status}`); return { action: utils_1.PaymentActions.FAILED, data: { session_id: sessionId, amount: amount, }, }; } console.log("Transaction verified successfully"); const { medusaStatus } = await this.getTransactionDetailsAndStatus(sessionId); const webhookData = { session_id: sessionId, amount: amount, }; switch (medusaStatus) { case "authorized": return { action: utils_1.PaymentActions.AUTHORIZED, data: webhookData, }; case "captured": return { action: utils_1.PaymentActions.SUCCESSFUL, data: webhookData, }; case "pending": return { action: utils_1.PaymentActions.PENDING, data: webhookData, }; case "canceled": return { action: utils_1.PaymentActions.CANCELED, data: webhookData, }; case "error": return { action: utils_1.PaymentActions.FAILED, data: webhookData, }; default: return { action: utils_1.PaymentActions.NOT_SUPPORTED, data: webhookData, }; } } catch (error) { console.error("Error processing P24 webhook:", error); // Try to extract basic information even if there's an error try { const fallbackPayload = webhookData.data; const { sessionId, amount, currency } = fallbackPayload; if (sessionId && amount && currency) { return { action: utils_1.PaymentActions.FAILED, data: { session_id: sessionId, amount: amount, }, }; } } catch (fallbackError) { console.error("Failed to extract fallback data:", fallbackError); } throw error; } } /** * Gets the payment status from P24 based on the session ID. * This method fetches the current status from P24 and maps it to Medusa's format. * * @param input - The get payment status input containing session data * @returns The current payment status from P24 */ async getPaymentStatus(input) { const sessionId = input.context?.idempotency_key; if (!sessionId) { console.warn("No session ID provided for getPaymentStatus, returning pending"); return { status: "pending" }; } console.log(`Fetching payment status for session: ${sessionId}`); try { const { medusaStatus } = await this.getTransactionDetailsAndStatus(sessionId); return { status: medusaStatus }; } catch (error) { console.error(`Error getting payment status for session ${sessionId}: ${error.message}`); throw error; } } /** * Gets transaction details and maps status from P24 * This is a reusable method to avoid code duplication across payment methods * * @param sessionId - The P24 session ID * @returns Object containing transaction details and mapped Medusa status */ async getTransactionDetailsAndStatus(sessionId) { const transactionDetails = await this.p24Api.getTransactionBySessionId(sessionId); if (transactionDetails.responseCode !== 0) { throw this.buildError("Failed to retrieve transaction details", new Error(`P24 API error: ${transactionDetails.responseCode}`)); } const p24Status = parseInt(transactionDetails.data.status); const medusaStatus = this.mapP24StatusToMedusaStatus(p24Status); console.log(`P24 Status: ${p24Status} -> Medusa Status: ${medusaStatus}`); return { transactionDetails, p24Status, medusaStatus, }; } /** * Maps P24 transaction status to Medusa payment status * P24 statuses: 0 - no payment, 1 - advance payment, 2 - payment made, 3 - payment returned * Medusa statuses: "authorized" | "captured" | "pending" | "requires_more" | "error" | "canceled" */ mapP24StatusToMedusaStatus(p24Status) { switch (p24Status) { case 0: // no payment return "pending"; case 1: // advance payment return "authorized"; case 2: // payment made return "captured"; case 3: // payment returned return "canceled"; default: return "error"; } } buildError(message, error) { return new Error(`${message}: ${error?.message || "Unknown error"}`.trim()); } } exports.default = P24Base; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicDI0LWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3ByemVsZXd5MjQvY29yZS9wMjQtYmFzZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG9EQUE0QjtBQU01QixxREFJbUM7QUFTbkMsaURBQW9EO0FBdUJwRCxNQUFlLE9BQVEsU0FBUSwrQkFBbUM7SUFLaEUsTUFBTSxDQUFDLGVBQWUsQ0FBQyxPQUFtQjtRQUN4QyxJQUFJLENBQUMsSUFBQSxpQkFBUyxFQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1lBQ3BDLE1BQU0sSUFBSSxLQUFLLENBQUMsd0RBQXdELENBQUMsQ0FBQztRQUM1RSxDQUFDO1FBQ0QsSUFBSSxDQUFDLElBQUEsaUJBQVMsRUFBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUMvQixNQUFNLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7UUFDdkUsQ0FBQztRQUNELElBQUksQ0FBQyxJQUFBLGlCQUFTLEVBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDaEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxvREFBb0QsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7UUFDRCxJQUFJLENBQUMsSUFBQSxpQkFBUyxFQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzVCLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztRQUNwRSxDQUFDO1FBQ0QsSUFBSSxDQUFDLElBQUEsaUJBQVMsRUFBQyxPQUFPLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUNyQyxNQUFNLElBQUksS0FBSyxDQUNiLHlEQUF5RCxDQUMxRCxDQUFDO1FBQ0osQ0FBQztRQUNELElBQUksQ0FBQyxJQUFBLGlCQUFTLEVBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDcEMsTUFBTSxJ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