PHP Chained Payments - Kopfzerbrechen

koffi

Lt. Junior Grade
Registriert
Jan. 2007
Beiträge
490
Hallo!

Ich stehe bei der Entwicklung einer Webseite vor einem Problem über welches ich mir seit Tagen den Kopf zerbreche. Mir machen "Denkspiele" solcher Art Spaß, aber dieses überschreitet ein wenig meine Hirnkapazität als Hobby-Webdesigner, deswegen hoffe ich hier professionelle Hilfe zu bekommen.

Auf meiner Webseite sollen Benutzer eine Art premium Mitgliedschaft kaufen können. Dieses Produkt biete ich in 6 verschiedenen Paketen von 1 bis 6 Jahren Mitgliedschaft an.

Hier ein Einblick in die Tabelle "payments":
Unbenannt.PNG
(ledeglich Test-Datensätze)

Ein Benutzer, welcher keine Premium-Mitgliedschaft hat, hat in meiner Datenbank einen "user_status" von "1".
Premium Mitglieder sollen einen Status von "2" haben. Den Status von "2" kann ein Benutzer jedoch nicht in der Datenbank haben, er errechnet sich ledeglich in PHP abhängig davon, ob er eine Zahlung in der Vergangenheit gemacht hat und diese momentan noch nicht abgelaufen ist.

So weit also noch nicht allzu kompliziert umzusetzen.

Ich möchte dem Benutzer jedoch die Möglichkeit bieten, seinen Premium-Status zu verlängern während er noch Premium Status hat. Diese neue Zahlung soll ihre Gültigkeit also folglich erst dann beginnen, wenn wenn die Laufzeit der alten Zahlung abgelaufen ist.
In meiner Datenbank möchte ich jedoch auf ein weiteres Feld wie "gültig ab" verzichten, da womöglich alte Zahlungen annuliert werden und die neuere dann sofort greifen soll. Außerdem denke ich, dass das eine unsaubere Lösung wäre, da ja der Ablauf dr Premium Mitgliedschaft immer abhängig von allen Zahlungen berechnet werden kann.

Ich habe hier mal ein Beispiel gemacht..
Unbenann2t.PNG

In diesem Beispiel ist veranschaulicht, dass ein Benutzer mitte 2014 mal ein Jahr Premium Mitgliedschaft gekauft hat. Danach war er kurz wieder Basis-Mitglied, und hat kurz vor 2015 ein 2-Jahres Paket gekauft. Dieses würde 2017 ablaufen, davor hat er jedoch erneut 1 Jahr gekauft, und dann erneut 1 Jahr. Seine Mitgliedschaft läuft also erst irgendwann 2019 ab, nicht 2018 wie es in der Grafik steht. Die Zahlungen sollen sich also aneinanderreihen.

Hier eine Grafik, wie die Zahlungen theoretisch aneinandergereiht werden sollen, um das tatsächliche Verfallsdatum zu berechnen.
Unbenannt3.PNG

In PHP habe ich eine Klasse für Zahlungen, diese beinhaltet Informationen wie: Zahlungsdatum, Produkt-Laufzeit und relatives Verfallsdatum (relativ, da das Verfallsdatum ja von früher getätigten, noch aktiven Zahlungen abhängig ist).

Hier also mein Ansatz:

Code:
<?php
private
function determine_status($id)
	{

	// user status in db can never be 2 or 3, those are calculated from payments

	if ($id == 1)
		{

		// get all payments to this user

		$payments_received = array();
		foreach($this->user->payments as $payment)
			{
			if ($payment->benefited_id == $this->user->id)
				{
				array_push($payments_received, $payment);
				}
			}

		// chain all payments

		$payment_chains = array();
		foreach($payments_received as $payment_received)
			{
			}
		}
	  else
		{
		return $id;
		}
	}

Leider stehe ich total auf dem Schlauch wie ich das nun zu berechnen habe...
Ich hoffe das war einigermaßen verstädnlich und jemand kann mir einen Denkanstoß geben!

Tausen Dank im vorraus schonmal!
 
Hier meine Idee, in Javascript zusammengezimmert:

Code:
var end = 0;

//payments have to be sorted by confirmed
for (var i = 0; i < payments.length; i++) {
	var payment = payments[i];

	if(payment.confirmed == 0)
		continue;
	
	if (payment.confirmed > end) {
		end = payment.confirmed + (payment.duration * 365 * 24 * 60 * 60); //convert years to seconds for unix-timestamp
	}
	else {
		end += payment.duration * 365 * 24 * 60 * 60; //convert years to seconds for unix-timestamp
	}
}

if (end >= Date.now()){
//user is premium
}

bin mir nicht sicher, ob es so funktionieren würde.
 
Ich würde zwar jedes einzelne Payment speichern, also wann, wie lange bezahlt wurde, aber zum Benutzer in der db eine neue Variable mit dem Enddatum der Premium-Zeit, welche beim Kauf um x*356 Tage erhöht wird.
 
Premium zerfällt mit konstanter Rate. Berechne, ob die Restkapazität größer 0 ist.

Oder einfacher: Zwei Werte: Betrag und Datum.
Beide zusammen definieren den aktuellen Premiumstand
Code:
select Betrag-(NOW()-Datum)*geld_pro_zeit>0 as Premium
Und wenn du eine Zahlung einträgst
Code:
update premium set Betrag=max(Betrag-(NOW()-Datum)*geld_pro_zeit,0)+neu_betrag
Ist jetzt in SQL, ich denke, die Idee ist klar. Kompliziert wird's, wenn du verschiedene "Zerfallsraten" hast, je nachdem wie viel man gekauft hat, aber da könntest du das beim Bezahlen kompensieren, indem du da die Differenz auf Betrag draufschlägst.

Live aus den Transaktionen den Status rausrechnen könnte kompliziert werden (mit SQL, in einem Query).

EDIT: Beim Nachdenken ist thinkpadX201's Idee eleganter (und genauso mächtig).
 
Zuletzt bearbeitet:
Du speicherst zu jeder Zahlung die Informationen: Erstellungsdatum (also Zahlungseingang) und Dauer (in Jahren). Zudem gehe ich mal davon aus, dass die Zahlungen nach Erstellungsdatum sortiert sind (wäre ja logisch, ansonsten order by).

Ein einfacher Algorithmus (Pseudocode)
Code:
DateTime Premium-EndeMAX = Jetzt;
DateTime Premium-Ende = Jetzt;
foreach (var pmt in SQLQuery("Select Created, Confirmed, Duration from Payments where benifit_id = user.id")){
    if(pmt.Confirmed!=0){
        Premium-Ende += Premium-Ende - Created + Duration;
        if(Premium-Ende > Premium-EndeMAX) Premium-EndeMAX = Premium-Ende;
    }
}
return Premium-EndeMAX;

Die Funktion gibt "Jetzt" zurück, wenn keine gültige Zahlung mehr vorliegt, anderenfalls das Ende-Datum. Ein vorzeitiger Abbruch ist hier nicht möglich, warum sollte klar sein, wenn man etwas Erfahrung mit Programmieren hat :)
Willst du nicht das Ende des Premiums herausfinden, sondern nur, ob der User Premium ist, dann kannst du an der Stelle, an der Premium-EndeMAX erhöht wird stattdessen direkt aus der Funktion rausspringen und true zurückgeben.
 
dank eurer hilfe hab ich es hinbekommen. letztendlich garnicht so schwer, ich hab mir das ein bisschen zu kompliziert vorgestellt.
letztendlich muss man ja nur alle zahlungen in chronologischer reihenfolge durchgehen. falls eine zahlung zu diesem zeitpunk gültig ist, setzt man ein neues ablauf-datum, und falls schon ein ablauf-datum gesetzt war, addiert man einfach die zeit dazu.

Code:
	private function determine_status($id) {
		//user status in db can never be 2 or 3, those are calculated from payments
		if ($id == 1) {
			else {
				if ($this -> expiration_date != false) {					
					return 2;
				} else {
					return $id;
				}
		} else {
			return $id;
		}
	}

Code:
private function get_expiration_date($payments) {
		//get all payments to this user
		$payments_received = array();
		foreach ($payments as $payment) {
			if ($payment -> benefited_id == $this -> user -> id) {
				array_push($payments_received, $payment);
			}
		}

		//sort all payments by date, ASC
		usort($payments_received, function($a, $b) {
			return $a -> confirmed - $b -> confirmed;
		});

		$expiration_date = false;

		foreach ($payments_received as $payment_received) {
			//check if is valid
			if ($payment_received -> confirmed <= time() + $payment_received -> duration * year) {
				//check if expiration date was set before
				if ($expiration_date != false) {
					//add the time to the expiration date
					$expiration_date += $payment_received -> duration * year;
				} else {
					//set the expiration date
					$expiration_date = $payment_received -> confirmed + ($payment_received -> duration * year);
				}
			}
		}
		return $expiration_date;
	}

ich hoffe ich habe da jetzt keinen unerwarteten fall übersehen, der eintreten könnte, scheint mir aber solide.
tausend dank an alle!
 
Zurück
Oben