<?php
/*
 * stripe_event_invoice_c.php — Invoice event handler
 *
 * Handled event types ($event_json->type):
 *   invoice.upcoming
 *   invoice.finalized
 *   invoice.payment_succeeded
 *   invoice.payment_failed
 *   invoice.marked_uncollectible
 */

require_once 'stripe_database.php';
require_once 'KLogger.php';

$DbObject    = new MySQLDB;
$MyLogger    = new KLogger(STRIPE_LOG_FILE_INVOICE, KLogger::DEBUG);
$MyRawLogger = STRIPE_RAW_LOGGING ? new KLogger(STRIPE_RAW_LOG_FILE_INVOICE, KLogger::DEBUG) : null;
require_once STRIPE_INIT_LOCATION;

\Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY);

$Handled   = false;
$Body      = @file_get_contents('php://input');
$SigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';

try {
    $EventJson = \Stripe\Webhook::constructEvent($Body, $SigHeader, STRIPE_WEBHOOK_SECRET_INVOICE);
} catch (\UnexpectedValueException $E) {
    $MyLogger->logDebug("Rejected request: invalid payload");
    http_response_code(400);
    exit;
} catch (\Stripe\Exception\SignatureVerificationException $E) {
    $MyLogger->logDebug("Rejected request: invalid signature");
    http_response_code(400);
    exit;
}
http_response_code(200);
if ($MyRawLogger) $MyRawLogger->logDebug("RAW_EVENT_JSON: " . $Body);
$MyLogger->logDebug("event: " . $EventJson->type);

if ($EventJson->type == 'invoice.upcoming') {
    $Handled    = true;
    $Invoice    = $EventJson->data->object;
    $CustomerID = $Invoice->customer ?? '';
    $PeriodEndDate  = date('Y-m-d', $Invoice->period_end ?? 0) ?: null;
    $SubscriptionID = getSubscriptionIDFromInvoice($Invoice);

    $DbObject->saveUpcomingStripeInvoice($Invoice, $EventJson->created);

    $MyLogger->logDebug("invoice.upcoming | CustomerID: " . $CustomerID . " | SubscriptionID: " . $SubscriptionID.", Period End: ". $PeriodEndDate);
}

if ($EventJson->type == 'invoice.finalized') {
    $Handled = true;

    $Invoice        = $EventJson->data->object;
    $InvoiceID      = $Invoice->id           ?? '';
    $InvoiceNumber  = $Invoice->number       ?? '';
    $CustomerID     = $Invoice->customer     ?? '';
    $SubscriptionID = getSubscriptionIDFromInvoice($Invoice);
    $Amount         = ($Invoice->total ?? 0) / 100;
    $InvoiceDate    = date('Y-m-d H:i:s', $Invoice->created ?? $EventJson->created);
    $CreateDate     = date('Y-m-d H:i:s', $EventJson->created);
    $PeriodEndDate  = date('Y-m-d', $Invoice->period_end ?? 0) ?: null;

    // No payment has been attempted at finalize time, so there is no charge to
    // resolve.  invoice.payment_succeeded / invoice.payment_failed fill in the
    // ChargeID once Stripe actually attempts payment.
    $ChargeID = '';

    $DbObject->updateStripeInvoice(
        $InvoiceNumber, $CustomerID, $SubscriptionID, $Amount,
        '', $CreateDate, $InvoiceDate, null,
        $InvoiceID, $ChargeID, $Invoice, $PeriodEndDate
    );
    
    $MyLogger->logDebug("invoice.finalized | InvoiceID: " . $InvoiceID . " | CustomerID: " . $CustomerID . " | Amount: " . $Amount.", Period End: ". $PeriodEndDate);
}

if ($EventJson->type == 'invoice.payment_succeeded') {
    $Handled = true;

    $Invoice        = $EventJson->data->object;
    $InvoiceID      = $Invoice->id           ?? '';
    $InvoiceNumber  = $Invoice->number       ?? '';
    $CustomerID     = $Invoice->customer     ?? '';
    $SubscriptionID = getSubscriptionIDFromInvoice($Invoice);
    $Amount         = ($Invoice->total ?? 0) / 100;
    $InvoiceDate    = date('Y-m-d H:i:s', $Invoice->created ?? $EventJson->created);
    $CreateDate     = date('Y-m-d H:i:s', $EventJson->created);
    $ChargeDate     = date('Y-m-d H:i:s', $EventJson->created);
    $PeriodEndDate  = date('Y-m-d', $Invoice->period_end ?? 0) ?: null;

    // Invoice->charge was removed in the Stripe "basil" release — resolve it
    // through the invoice's InvoicePayment -> PaymentIntent -> latest_charge.
    $PaymentIntentID = '';
    $FailureMessage  = '';
    $ChargeID        = '';
    $CardType        = '';
    $CardBrand       = '';
    $CardLastFour    = '';
    GetInvoicePaymentAttempt(
        $InvoiceID, $PaymentIntentID, $ChargeID, $FailureMessage,
        $CardType, $CardBrand, $CardLastFour
    );

    $DbObject->updateStripeInvoice(
        $InvoiceNumber, $CustomerID, $SubscriptionID, $Amount,
        '', $CreateDate, $InvoiceDate, $ChargeDate,
        $InvoiceID, $ChargeID, $Invoice, $PeriodEndDate
    );

    $MyLogger->logDebug("invoice.payment_succeeded | InvoiceID: " . $InvoiceID . " | CustomerID: " . $CustomerID . " | ChargeID: " . $ChargeID . " | PaymentIntentID: " . $PaymentIntentID.", Period End: ". $PeriodEndDate);
}

if ($EventJson->type == 'invoice.payment_failed') {
    $Handled = true;

    $Invoice        = $EventJson->data->object;
    $InvoiceID      = $Invoice->id           ?? '';
    $InvoiceNumber  = $Invoice->number       ?? '';
    $CustomerID     = $Invoice->customer     ?? '';
    $SubscriptionID = getSubscriptionIDFromInvoice($Invoice);
    $Amount         = ($Invoice->total ?? 0) / 100;
    $InvoiceDate    = date('Y-m-d H:i:s', $Invoice->created ?? $EventJson->created);
    $CreateDate     = date('Y-m-d H:i:s', $EventJson->created);
    $ChargeDate     = date('Y-m-d H:i:s', $EventJson->created);
    $PeriodEndDate  = date('Y-m-d', $Invoice->period_end ?? 0) ?: null;
    $AttemptCount   = (int) ($Invoice->attempt_count ?? 0);

    // Invoice->charge was removed in the Stripe "basil" release — resolve it
    // through the invoice's InvoicePayment -> PaymentIntent -> latest_charge.
    // A decline at the PaymentIntent level produces no Charge at all, so
    // $ChargeID may legitimately be '' while $FailureMessage is populated.
    $PaymentIntentID = '';
    $FailureMessage  = '';
    $ChargeID        = '';
    $CardType        = '';
    $CardBrand       = '';
    $CardLastFour    = '';
    GetInvoicePaymentAttempt(
        $InvoiceID, $PaymentIntentID, $ChargeID, $FailureMessage,
        $CardType, $CardBrand, $CardLastFour
    );

    $DbObject->updateStripeInvoice(
        $InvoiceNumber, $CustomerID, $SubscriptionID, $Amount,
        '', $CreateDate, $InvoiceDate, $ChargeDate,
        $InvoiceID, $ChargeID, $Invoice, $PeriodEndDate
    );

    // Record the failed attempt in StripePayments.  This event — not charge.failed —
    // is the authoritative source, because a decline at the PaymentIntent level
    // never produces a Charge and therefore never fires charge.failed at all.
    //
    // EventResult carries the decline reason appended to the status, e.g.
    // "Failed: Your card has insufficient funds."  Existing consumers match
    // `like 'Fail%'` / `like 'Failed%'`, so the suffix is safe.
    $TenantID    = ($CustomerID !== '') ? $DbObject->getTenantIDFromStripeID($CustomerID) : -1;
    $EventResult = ($FailureMessage !== '') ? 'Failed: ' . $FailureMessage : 'Failed';

    $DbObject->suspendTenantReporting($TenantID);
    $DbObject->saveChargeResult(
        $TenantID, $CustomerID, $ChargeID, $PaymentIntentID,
        $EventJson->created, $Amount, $CardType, $CardBrand, $CardLastFour,
        $EventResult, $InvoiceID, $FailureMessage
    );

    if ($CardBrand !== '' || $CardLastFour !== '') {
        $DbObject->updateTenantCardInfo($CustomerID, $CardType, $CardBrand, $CardLastFour, 0, 0);
    }

    if ($TenantID > 0) {
        $DbObject->logUserEvent("Invoice payment failed: " . $InvoiceID . " attempt " . $AttemptCount . " — " . $EventResult, $TenantID);
    }

    $MyLogger->logDebug("invoice.payment_failed | InvoiceID: " . $InvoiceID . " | CustomerID: " . $CustomerID . " | ChargeID: " . $ChargeID . " | PaymentIntentID: " . $PaymentIntentID . " | AttemptCount: " . $AttemptCount . " | EventResult: " . $EventResult.", Period End: ". $PeriodEndDate);
}

if ($EventJson->type == 'invoice.marked_uncollectible') {
    $Handled = true;

    $Invoice   = $EventJson->data->object;
    $InvoiceID = $Invoice->id       ?? '';
    $CustomerID = $Invoice->customer ?? '';
    $PeriodEndDate  = date('Y-m-d', $Invoice->period_end ?? 0) ?: null;
    $DbObject->markInvoiceForgiven($InvoiceID);

    $MyLogger->logDebug("invoice.marked_uncollectible | InvoiceID: " . $InvoiceID . " | CustomerID: " . $CustomerID.", Period End: ". $PeriodEndDate);
}

if ($Handled == false) {
    $MyLogger->logDebug("unhandled EVENT CONTENTS: " . print_r($EventJson, true));
}
?>
