Connexion tocken, stripe 3D, tracking analytic

This commit is contained in:
Oleg Barykin committed 2026-03-11 21:44:19 +01:00
1 parent 1deb30fcd1
commit bd311da711
17 files changed
+3324 -455

No files matched your search

+54 -42
View File
@@ -644,36 +644,13 @@ if (in_array($type, $checkoutEvents, true) && $obj) {
'last_event_id' => $eventId,
]);
// 1) Aligner praticien (abonnement_id + date_expiration)
if ($praticien_id && $abonnement_id && $current_period_end && colExists($pdo, 'praticien', 'date_expiration')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, date_expiration = ? WHERE id = ?")
->execute([(int) $abonnement_id, (string) $current_period_end, (int) $praticien_id]);
} elseif ($praticien_id && $abonnement_id && $current_period_end && colExists($pdo, 'praticien', 'abonnement_expire_le')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, abonnement_expire_le = ? WHERE id = ?")
->execute([(int) $abonnement_id, (string) $current_period_end, (int) $praticien_id]);
}
// IMPORTANT:
// Ne pas activer/prolonger l'accès ici.
// L'accès réel est accordé uniquement sur invoice.paid.
// 2) Créer le paiement même si invoice.paid est arrivé avant (robuste aux webhooks désordonnés)
try {
if ($sub_id) {
$subLatest = $stripe->subscriptions->retrieve((string) $sub_id, []);
$latestInvoiceId = stripeId($subLatest->latest_invoice ?? null);
if ($latestInvoiceId) {
$invLatest = $stripe->invoices->retrieve((string) $latestInvoiceId, []);
insertPaiementFromInvoice($pdo, $configAll, $stripe, $invLatest, (int) $praticien_id, (int) $abonnement_id, (string) $sub_id, $eventId);
// Optionnel: stocker last_invoice_id dans billing
$upsertBilling([
'praticien_id' => $praticien_id,
'last_invoice_id' => (string) $latestInvoiceId,
'last_event_id' => $eventId,
]);
}
}
} catch (Throwable $e) {
wh_log('WARN checkout-sub latest_invoice insert paiement failed: ' . $e->getMessage());
}
// IMPORTANT:
// Ne pas créer de ligne paiement ici pour un abonnement.
// La création du paiement réel se fait uniquement sur invoice.paid.
http_response_code(200);
echo 'ok-checkout-subscription';
@@ -810,6 +787,7 @@ if (in_array($type, $checkoutEvents, true) && $obj) {
$billingEvents = [
'invoice.paid',
'invoice.payment_failed',
'invoice.payment_action_required',
'invoice_payment.paid', // type vu chez toi (non standard)
'customer.subscription.updated',
'customer.subscription.deleted',
@@ -822,7 +800,12 @@ if (in_array($type, $billingEvents, true) && $obj) {
}
// ---------- invoice.* ----------
if ($type === 'invoice.paid' || $type === 'invoice.payment_failed' || $type === 'invoice_payment.paid') {
if (
$type === 'invoice.paid' ||
$type === 'invoice.payment_failed' ||
$type === 'invoice.payment_action_required' ||
$type === 'invoice_payment.paid'
) {
// invoice_payment.paid => récupérer la vraie invoice
if ($type === 'invoice_payment.paid') {
@@ -870,18 +853,30 @@ if (in_array($type, $billingEvents, true) && $obj) {
$amount_cent = is_numeric($inv->amount_due ?? null) ? (int) $inv->amount_due : null;
}
// Price/Product depuis lignes
$provider_plan_id = null;
$provider_product_id = null;
// Price/Product + période depuis lignes
$provider_plan_id = null;
$provider_product_id = null;
$invoice_period_start = null;
$invoice_period_end = null;
try {
if (isset($inv->lines->data) && is_array($inv->lines->data) && ! empty($inv->lines->data)) {
$line0 = $inv->lines->data[0];
if (isset($line0->price)) {
$provider_plan_id = stripeId($line0->price);
if (is_object($line0->price) && isset($line0->price->product)) {
$provider_product_id = stripeId($line0->price->product);
}
}
if (isset($line0->period)) {
if (isset($line0->period->start)) {
$invoice_period_start = dtFromUnix($line0->period->start);
}
if (isset($line0->period->end)) {
$invoice_period_end = dtFromUnix($line0->period->end);
}
}
}
} catch (Throwable $e) {
wh_log('WARN invoice lines parse: ' . $e->getMessage());
@@ -1046,7 +1041,14 @@ if (in_array($type, $billingEvents, true) && $obj) {
}
}
// Pour un renouvellement, la période portée par la facture est souvent
// la source la plus fiable pour prolonger l'accès.
if ($invoice_period_start) {
$current_period_start = $invoice_period_start;
}
if ($invoice_period_end) {
$current_period_end = $invoice_period_end;
}
wh_log("BILL ev=$eventId $type inv=" . ($invoice_id ?: 'null') . " sub=" . ($sub_id ?: 'null') . " cust=" . ($cust_id ?: 'null') . " pid=" . ($praticien_id ?: 'null') . " aid=" . ($abonnement_id ?: 'null') . " cps=" . ($current_period_start ?: 'null') . " cpe=" . ($current_period_end ?: 'null') . " status=" . ($sub_status ?: ''));
// Upsert billing_subscriptions (NE PAS écraser avec null)
@@ -1059,7 +1061,11 @@ if (in_array($type, $billingEvents, true) && $obj) {
'provider_subscription_id' => $sub_id,
'provider_plan_id' => $provider_plan_id,
'provider_product_id' => $provider_product_id,
'status' => $sub_status ?? ($type === 'invoice.payment_failed' ? 'payment_failed' : 'paid'),
'status' => $sub_status ?? (
$type === 'invoice.payment_failed'
? 'payment_failed'
: ($type === 'invoice.payment_action_required' ? 'action_required' : 'paid')
),
'current_period_start' => $current_period_start,
'current_period_end' => $current_period_end,
'cancel_at_period_end' => (int) $cancel_at_period_end,
@@ -1068,7 +1074,12 @@ if (in_array($type, $billingEvents, true) && $obj) {
'last_event_id' => $eventId,
'last_invoice_id' => $invoice_id,
]);
if ($type === 'invoice.payment_action_required') {
wh_log("ACTION_REQUIRED inv=" . ($invoice_id ?: 'null') . " sub=" . ($sub_id ?: 'null') . " cust=" . ($cust_id ?: 'null') . " pid=" . ($praticien_id ?: 'null'));
http_response_code(200);
echo 'ok-invoice-action-required';
exit;
}
// invoice.paid => insert paiement + update praticien si pid/aid OK
if ($type === 'invoice.paid') {
if (! $praticien_id || ! $abonnement_id) {
@@ -1092,7 +1103,7 @@ if (in_array($type, $billingEvents, true) && $obj) {
$prix = is_numeric($amount_cent) ? ((int) $amount_cent) / 100.0 : 0.0;
$statut = pickStatut($pdo, 'payé');
$date_expiration = $current_period_end;
$date_expiration = $invoice_period_end ?: $current_period_end;
if (! $date_expiration) {
[$cpsF, $cpeF] = periodFallbackFromPlan($pdo, (int) $abonnement_id);
$date_expiration = $cpeF ?: (new DateTime('now'))->modify("+30 days")->format('Y-m-d H:i:s');
@@ -1135,6 +1146,9 @@ if (in_array($type, $billingEvents, true) && $obj) {
if (colExists($pdo, 'praticien', 'date_expiration')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, date_expiration = ? WHERE id = ?")
->execute([(int) $abonnement_id, (string) $date_expiration, (int) $praticien_id]);
} elseif (colExists($pdo, 'praticien', 'abonnement_expire_le')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, abonnement_expire_le = ? WHERE id = ?")
->execute([(int) $abonnement_id, (string) $date_expiration, (int) $praticien_id]);
}
$pdo->commit();
@@ -1276,11 +1290,9 @@ if (in_array($type, $billingEvents, true) && $obj) {
'last_event_id' => $eventId,
]);
// align expiration praticien
if ($praticien_id && $current_period_end && colExists($pdo, 'praticien', 'date_expiration')) {
$pdo->prepare("UPDATE praticien SET date_expiration = ? WHERE id = ?")
->execute([(string) $current_period_end, (int) $praticien_id]);
}
// IMPORTANT:
// Ne pas modifier praticien.date_expiration ici.
// Seul invoice.paid doit prolonger l'accès effectif.
http_response_code(200);
echo 'ok-sub';