Payment.java

package com.example.orderplatform.payments.domain;

import com.example.orderplatform.BusinessRuleViolationException;
import com.example.orderplatform.Money;
import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.UUID;

/**
 * Framework-independent payment state used to enforce authorization transitions.
 *
 * @param id payment identifier
 * @param orderId related order identifier
 * @param status current payment status
 * @param amount expected amount
 * @param createdAt creation timestamp
 * @param authorizedAt authorization timestamp when authorized
 */
public record Payment(
        UUID id,
        UUID orderId,
        PaymentStatus status,
        Money amount,
        OffsetDateTime createdAt,
        OffsetDateTime authorizedAt) {

    public Payment {
        Objects.requireNonNull(id, "id is required");
        Objects.requireNonNull(orderId, "orderId is required");
        Objects.requireNonNull(status, "status is required");
        Objects.requireNonNull(amount, "amount is required");
        Objects.requireNonNull(createdAt, "createdAt is required");
        if (status == PaymentStatus.AUTHORIZED && authorizedAt == null) {
            throw new BusinessRuleViolationException("Authorized payments must include an authorization timestamp.");
        }
    }

    /**
     * Creates a pending payment for a submitted order.
     *
     * @param id generated payment identifier
     * @param orderId related order identifier
     * @param amount expected payment amount
     * @param createdAt creation timestamp
     * @return pending payment
     */
    public static Payment pending(UUID id, UUID orderId, Money amount, OffsetDateTime createdAt) {
        return new Payment(id, orderId, PaymentStatus.PENDING, amount, createdAt, null);
    }

    /**
     * Authorizes this payment when it is still pending.
     *
     * @param authorizedAt authorization timestamp
     * @return authorized payment state
     * @throws BusinessRuleViolationException when the payment is not pending
     */
    public Payment authorize(OffsetDateTime authorizedAt) {
        if (status != PaymentStatus.PENDING) {
            throw new BusinessRuleViolationException("Only pending payments can be authorized.");
        }
        return new Payment(id, orderId, PaymentStatus.AUTHORIZED, amount, createdAt, authorizedAt);
    }
}