PaymentTransaction

Overview

PaymentTransaction is the Mongoose model that stores each payment webhook event received by the backend. It records the related PaymentOrder, the provider, the event name, the provider’s external identifier, the raw webhook payload, and the processing time. The file defines this schema with timestamps: false, so MongoDB does not add automatic createdAt or updatedAt fields.

This model serves as an immutable audit record for payment events. The source comment explicitly describes it as a “registro inmutable de cada evento de webhook recibido”, and the payments service creates one record after confirming a payment. fileciteturn0file0turn0file11

[!NOTE] PaymentTransaction is not the payment order itself. It is the historical log of the webhook event that affected that order. fileciteturn0file0turn0file11

How it works

flowchart TB
    Webhook[Webhook recibido] --> Service[payments service]
    Service --> Order[PaymentOrder]
    Service --> Tx[PaymentTransaction]
    Tx --> MongoDB[MongoDB]
    Order --> MongoDB

The schema is created with Mongoose and exported as the PaymentTransaction model. In the payment confirmation flow, payments.service.js first finds the corresponding PaymentOrder, marks it as paid, and then creates a PaymentTransaction document with the webhook metadata and raw payload. fileciteturn0file0turn0file11

The model itself does not contain business logic. Its responsibility is persistence and traceability. The service layer supplies the values, and the schema constrains how they are stored. fileciteturn0file0turn0file11

Key components

{
    "title": "PaymentTransaction",
    "content": "Mongoose model that persists each received payment webhook event. It links the event to a PaymentOrder and stores the raw provider payload for audit and replay purposes.",
    "type": "class",
    "filePath": "services/api/src/models/PaymentTransaction.js",
    "badges": [
        "public"
    ]
}

Data & fields

[
    {
        "name": "paymentOrderId",
        "type": "Schema.Types.ObjectId",
        "required": true,
        "description": "Reference to the related PaymentOrder document."
    },
    {
        "name": "provider",
        "type": "String",
        "required": true,
        "description": "Payment provider that emitted the event. Allowed values are stripe and mercadopago."
    },
    {
        "name": "event",
        "type": "String",
        "required": true,
        "description": "Event type as reported by the provider. The value is trimmed."
    },
    {
        "name": "externalId",
        "type": "String",
        "description": "External payment or event identifier from the provider. Defaults to an empty string and is trimmed."
    },
    {
        "name": "rawPayload",
        "type": "Schema.Types.Mixed",
        "description": "Complete webhook payload stored for audit and possible reprocessing. Defaults to an empty object."
    },
    {
        "name": "processedAt",
        "type": "Date",
        "description": "Processing timestamp. Defaults to the current date and time when the document is created."
    }
]
  • paymentOrderId uses ref: "PaymentOrder", so each transaction points to one payment order.
  • provider only accepts the enum values stripe and mercadopago.
  • event is required and trimmed before storage.
  • externalId defaults to "" when the webhook does not provide one.
  • rawPayload accepts any Mongoose Mixed value, which allows the original webhook structure to be preserved.
  • processedAt is set with () => new Date() at creation time.

Behaviors

  • mongoose.model("PaymentTransaction", PaymentTransactionSchema) — registers the schema as the PaymentTransaction model and exports it.
  • timestamps: false — disables automatic timestamp fields on the schema.
  • processedAt default function — assigns the current date when the document is created.
  • rawPayload default value — stores an empty object when no payload is provided.

Workflows

1. Receive webhook | The backend receives a payment event from the provider.
2. Resolve order | The payments service locates the related PaymentOrder.
3. Confirm payment | The service marks the order as paid and records payment metadata.
4. Persist audit record | The service creates a PaymentTransaction with the provider, event, externalId, and rawPayload.

The observable use of this model appears in services/api/src/modules/payments/payments.service.js. That service creates PaymentTransaction after a successful payment confirmation, which makes the model part of the payment audit trail. fileciteturn0file11turn0file18

Gotchas & edge cases

[!WARNING] The schema has timestamps: false. There are no automatic createdAt or updatedAt fields.

[!IMPORTANT] rawPayload stores the complete webhook body. The model preserves the original event structure for audit and reprocessing needs. fileciteturn0file0turn0file11

[!NOTE] externalId may be empty. The schema sets default: "", and the service also falls back to an empty string when needed. fileciteturn0file0turn0file11

PaymentTransaction connects directly to services/api/src/models/PaymentOrder.js through paymentOrderId, and the payment service creates it during confirmation flows. The available context also shows PaymentOrder as the order record that gets updated before the transaction entry is stored. fileciteturn0file0turn0file11turn0file18

  • services/api/src/models/PaymentOrder.js — source of the referenced payment order.
  • services/api/src/modules/payments/payments.service.js — creates the transaction record after confirming payment.
  • services/api/src/modules/payments/providers/stripe.provider.js — part of the payment flow that supplies Stripe-related event data in the broader payments module. fileciteturn0file11turn0file18

🧾 In practice, this model preserves the technical history of each payment webhook that the backend processes.