// ADR 0009: pago real via PayU LatAm (PSE), modulo custom del backend // (workloads/commerce-backend/src/modules/payu-pse). Archivo separado de // medusa-cart.ts por el mismo motivo que ese archivo esta separado de // medusa.ts -- reutiliza su mismo fetch helper (medusaCartFetch), no // duplica la logica de headers/no-store. import { medusaCartFetch } from "./medusa-cart"; export interface PseBank { pseCode: string; description: string; } export type PseUserType = "N" | "J"; export type PseDocType = "CC" | "CE" | "NIT" | "TI" | "PP"; export interface PsePaymentInput { bankCode: string; userType: PseUserType; docType: PseDocType; docNumber: string; fullName: string; email: string; phone: string; address: string; city: string; state: string; } // "pp_{identifier}_{id}" -- convencion de Medusa, confirmada contra el // codigo fuente de @medusajs/payment (identifier "payu" + id "pse" en // medusa-config.ts). const PSE_PROVIDER_ID = "pp_payu_pse"; export async function getPseBanks(): Promise { const data = await medusaCartFetch("/store/payments/pse-banks"); const banks = (data.banks as Array<{ pseCode?: string; description?: string }> | undefined) ?? []; return banks .filter((bank): bank is { pseCode: string; description: string } => Boolean(bank.pseCode && bank.description)) .map((bank) => ({ pseCode: bank.pseCode, description: bank.description })); } export async function createPaymentCollection(cartId: string): Promise { const data = await medusaCartFetch("/store/payment-collections", { method: "POST", body: { cart_id: cartId }, }); const collection = data.payment_collection as { id: string }; return collection.id; } export async function createPsePaymentSession(paymentCollectionId: string, input: PsePaymentInput): Promise { const data = await medusaCartFetch(`/store/payment-collections/${paymentCollectionId}/payment-sessions`, { method: "POST", body: { provider_id: PSE_PROVIDER_ID, data: { bank_code: input.bankCode, user_type: input.userType, doc_type: input.docType, doc_number: input.docNumber, full_name: input.fullName, email: input.email, phone: input.phone, address: input.address, city: input.city, state: input.state, }, }, }); const collection = data.payment_collection as { payment_sessions?: Array<{ provider_id: string; data?: { bank_url?: string } }>; }; const session = collection.payment_sessions?.find((paymentSession) => paymentSession.provider_id === PSE_PROVIDER_ID); const bankUrl = session?.data?.bank_url; if (!bankUrl) { throw new Error("PayU no devolvió la URL del banco para completar el pago."); } return bankUrl; } // El carrito se completa (orden creada en estado "awaiting") ANTES de // redirigir al banco -- authorizePayment del modulo PSE devuelve // "pending_authorization" a proposito, que es justo el mecanismo de // Medusa para esto (ver AbstractPaymentProvider). La confirmacion real // llega despues via el webhook nativo de Medusa, no por esta llamada. export async function completeCart(cartId: string): Promise { const data = await medusaCartFetch(`/store/carts/${cartId}/complete`, { method: "POST" }); if (data.type === "cart") { const error = (data as { error?: { message?: string } }).error; throw new Error(error?.message || "No se pudo completar el pedido."); } }