Heim Backend-Entwicklung PHP-Tutorial PHP-Zahlungssystemdesign und typische Fälle (empfohlen)

PHP-Zahlungssystemdesign und typische Fälle (empfohlen)

Jun 01, 2018 pm 03:56 PM
php 推荐

In diesem Artikel werden hauptsächlich das Design und typische Fälle des PHP-Zahlungssystems im Detail vorgestellt. Es kann als kleines Zahlungssystem oder als Zahlungsflusssystem verwendet werden, wenn Anwendungen von Drittanbietern mit der offenen Plattform verbunden sind Interessierte Freunde können sich an

Aufgrund der geschäftlichen Anforderungen des Unternehmens dauerte die Implementierung eines kleinen Zahlungssystems. Obwohl es klein ist, verfügt es über alle erforderlichen Module, wie z. B. Kontosperre und Transaktionsgarantie , Fließwasserabgleich usw. sind im gesamten Entwicklungsprozess vollständig umgesetzt. Darüber hinaus handelt es sich bei den meisten davon um Forschungsarbeiten, die wenig praktischen Wert haben Ich habe es speziell herausgenommen, um es mit Ihnen zu teilen.
Dieses System kann als kleines Zahlungssystem oder als Zahlungsflusssystem verwendet werden, wenn Anwendungen von Drittanbietern an die offene Plattform angeschlossen werden.
Die ursprüngliche Forderung ist verantwortungsvoller, ich werde sie etwas vereinfachen:

Für jede Anwendung müssen externe Anforderungen bereitgestellt werden Guthaben erhalten, Geräte bezahlen, aufladen Warten Sie auf die Schnittstelle
Es gibt ein Programm im Hintergrund und die Liquidation wird am Ersten eines jeden Monats durchgeführt
Konten können eingefroren werden
Es ist notwendig, den Ablauf jedes Vorgangs aufzuzeichnen , und der Tagesablauf muss mit dem Initiator abgestimmt werden

Angesichts der oben genannten Anforderungen haben wir folgende Datenbank eingerichtet:

CREATE TABLE `app_margin`.`tb_status` ( 
`appid` int(10) UNSIGNED NOT NULL, 
`freeze` int(10) NOT NULL DEFAULT 0, 
`create_time` datetime NOT NULL, 
`change_time` datetime NOT NULL, 
  
PRIMARY KEY (`appid`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
CREATE TABLE `app_margin`.`tb_account_earn` ( 
`appid` int(10) UNSIGNED NOT NULL, 
`create_time` datetime NOT NULL, 
`balance` bigint(20) NOT NULL, 
`change_time` datetime NOT NULL, 
`seqid` int(10) NOT NULL DEFAULT 500000000, 
  
PRIMARY KEY (`appid`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
CREATE TABLE `app_margin`.`tb_bill` ( 
`id` int AUTO_INCREMENT NOT NULL, 
`bill_id` int(10) NOT NULL, 
`amt` bigint(20) NOT NULL, 
`bill_info` text, 
  
`bill_user` char(128), 
`bill_time` datetime NOT NULL, 
`bill_type` int(10) NOT NULL, 
`bill_channel` int(10) NOT NULL, 
`bill_ret` int(10) NOT NULL, 
  
`appid` int(10) UNSIGNED NOT NULL, 
`old_balance` bigint(20) NOT NULL, 
`price_info` text, 
  
`src_ip` char(128), 
  
PRIMARY KEY (`id`), 
UNIQUE KEY `unique_bill` (`bill_id`,`bill_channel`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
CREATE TABLE `app_margin`.`tb_assign` ( 
`id` int AUTO_INCREMENT NOT NULL, 
`assign_time` datetime NOT NULL, 
  
PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
CREATE TABLE `app_margin`.`tb_price` ( 
`name` char(128) NOT NULL, 
`price` int(10) NOT NULL, 
`info` text NOT NULL, 
  
PRIMARY KEY (`name`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
CREATE TABLE `app_margin`.`tb_applock` ( 
`appid` int(10) UNSIGNED NOT NULL, 
`lock_mode` int(10) NOT NULL DEFAULT 0, 
`change_time` datetime NOT NULL, 
  
PRIMARY KEY (`appid`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  
INSERT `app_margin`.`tb_assign` (`id`,`assign_time`) VALUES (100000000,now());
Nach dem Login kopieren

Die detaillierte Erklärung lautet wie folgt:
tb_status Anwendungsstatustabelle. Verantwortlich dafür, ob das Konto eingefroren ist und um welche Art von Konto es sich handelt (die eigentliche Anforderung besteht darin, dass die Anwendung zwei Konten haben darf, daher wird sie der Einfachheit halber hier nicht aufgeführt)
Appid-Anwendungs-ID
einfrieren, ob eingefroren werden soll
create_time Erstellungszeit
change_time letzte Änderungszeit
tb_account_earn Anwendungskontostandstabelle
appid Anwendungs-ID
Saldosaldo (Einheit ist Cent, verwenden Sie keinen Dezimalspeicher, da die Dezimalzahl selbst nicht genau ist; Darüber hinaus muss sich PHP auf einem 64-Bit-Computer befinden, der Bigint unterstützen kann >tb_assign-Tabelle zum Zuweisen der Serien-ID, bill_id von tb_bill. Muss durch tb_assign zugewiesen werden.
ID-Auto-Inkrement-ID
create_time-Erstellungszeit
tb_bill-Flusstabelle. Verantwortlich für die Aufzeichnung jedes Vorgangsablaufs ist hier nicht der Primärschlüssel, da die gleiche Rechnungsnummer zwei Flüsse haben kann:
ID-Auto-Inkrement-Sequenznummer
Rechnungs-ID-Seriennummer
Amtsbetrag Vorgang (dies ist notwendig, um zwischen positiv und negativ zu unterscheiden, hauptsächlich um die Änderung des Betrags während eines bestimmten Zeitraums direkt zu berechnen, wenn alle ausgewählt werden)
bill_info detaillierte Informationen des Vorgangs, z. B. 3 Webserver, 2 Datenbanken
bill_user Betriebsbenutzer
bill_time Die laufende Zeit
bill_type Der laufende Typ, der unterscheidet, ob Geld hinzugefügt oder abgezogen werden soll
bill_channel Die fließende Wasserquelle, wie Aufladen, Zahlung, Rollback, Abrechnung oder andere
bill_ret Der Rückgabecode des fließenden Wassers, einschließlich unverarbeitet und erfolgreich, ist fehlgeschlagen. Die Logik hier wird später erläutert Artikel, der bezahlt wird, wenn der Vorgang ausgeführt wird
src_ip client ip
tb_price Stückpreistabelle, zeichnet den Stückpreis der Maschine auf
Name eindeutige Kennung der Maschine
Preis Preis
Infobeschreibung
tb_applock-Sperrtabelle, die speziell dazu dient, gleichzeitige Schreibvorgänge für eine Anwendung zu vermeiden. Der Code zeigt später den Sperrstatus
appid application id
lock_mode an. Wenn es 0 ist, ist es gesperrt, wenn es 1 ist, ist es gesperrt
change_time Letzte Änderungszeit
OK, nachdem die Bibliothekstabelle entworfen wurde, werfen wir einen Blick auf einige der typischsten Vorgänge.



1. Zahlungsvorgang

Ich liste nur auf, wie ich es derzeit umsetze. Es ist vielleicht nicht das Beste, aber es sollte das wirtschaftlichste sein und den Bedürfnissen entsprechen.
Lassen Sie uns zuerst über den Anrufer sprechen:


Dann ist die entsprechende interne Logik des Zahlungssystems wie folgt (es werden nur Zahlungsvorgänge aufgeführt, Die Rollback-Logik ist ähnlich und die Flussprüfung besteht darin, zu überprüfen, ob der entsprechende Zahlungsfluss vorhanden ist):

Häufige Fehlerrückgabecodes können wie folgt ausreichen:

$g_site_error = array( 
-1 => '服务器繁忙', 
-2 => '数据库读取错误', 
-3 => '数据库写入错误', 
  
0 => '成功', 
  
1 => '没有数据', 
2 => '没有权限', 
3 => '余额不足', 
4 => '账户被冻结', 
5 => '账户被锁定', 
6 => '参数错误', 
);
Nach dem Login kopieren

对于大于0的错误都算是逻辑错误,执行支付操作,调用方是不用记录流水的。因为账户并没有发生任何改变。
对于小于0的错误是系统内部错误,因为不知道是否发生了数据更改,所以调用方和支付系统都要记录流水。
对于等于0的返回,代表成功,两边也肯定要记录流水。
而在支付系统内部,之所以采用先写入流水,再进行账户更新的方式也是有原因的,简单来说就是尽量避免丢失流水。
最后总结一下,这种先扣钱,再发货,出问题再回滚的方式是一种模式;还有一种是先预扣,后发货,没有出问题则调用支付确认来扣款,出了问题就调用支付回滚来取消,如果预扣之后很长时间不做任何确认,那么金额会自动回滚。

二. 账户锁定的实现
这里利用了数据库的加锁机制,具体逻辑就不说了,代码如下:

class AppLock 
{ 
function __construct($appid) 
{ 
$this->m_appid = $appid; 
//初始化数据 
$this->get(); 
} 
  
function __destruct() 
{ 
$this->free(); 
} 
  
  
public function alloc() 
{ 
if ($this->m_bGot == true) 
{ 
return true; 
} 
  
$this->repairData(); 
  
$appid = $this->m_appid; 
$ret = $this->update($appid,APPLOCK_MODE_FREE,APPLOCK_MODE_ALLOC); 
if ($ret === false) 
{ 
app_error_log("applock alloc fail"); 
return false; 
} 
if ($ret <= 0) 
{ 
app_error_log("applock alloc fail,affected_rows:$ret"); 
return false; 
} 
$this->m_bGot = true; 
return true; 
} 
  
public function free() 
{ 
if ($this->m_bGot != true) 
{ 
return true; 
} 
  
$appid = $this->m_appid; 
$ret = $this->update($appid,APPLOCK_MODE_ALLOC,APPLOCK_MODE_FREE); 
if ($ret === false) 
{ 
app_error_log("applock free fail"); 
return false; 
} 
if ($ret <= 0) 
{ 
app_error_log("applock free fail,affected_rows:$ret"); 
return false; 
} 
$this->m_bGot = false; 
return true; 
} 
  
function repairData() 
{ 
$db = APP_DB(); 
  
$appid = $this->m_appid; 
  
$now = time(); 
  
$need_time = $now - APPLOCK_REPAIR_SECS; 
  
$str_need_time = date("Y-m-d H:i:s", $need_time); 
  
$db->where("appid",$appid); 
$db->where("lock_mode",APPLOCK_MODE_ALLOC); 
$db->where("change_time <=",$str_need_time); 
  
$db->set("lock_mode",APPLOCK_MODE_FREE); 
$db->set("change_time","NOW()",false); 
  
$ret = $db->update(TB_APPLOCK); 
if ($ret === false) 
{ 
app_error_log("repair applock error,appid:$appid"); 
return false; 
} 
return true; 
} 
  
private function get() 
{ 
$db = APP_DB(); 
  
$appid = $this->m_appid; 
  
$db->where(&#39;appid&#39;, $appid); 
  
$query = $db->get(TB_APPLOCK); 
  
if ($query === false) 
{ 
app_error_log("AppLock get fail.appid:$appid"); 
return false; 
} 
  
if (count($query->result_array()) <= 0) 
{ 
$applock_data = array( 
&#39;appid&#39;=>$appid, 
&#39;lock_mode&#39;=>APPLOCK_MODE_FREE, 
); 
$db->set(&#39;change_time&#39;,&#39;NOW()&#39;,false); 
$ret = $db->insert(TB_APPLOCK, $applock_data); 
if ($ret === false) 
{ 
app_error_log("applock insert fail:$appid"); 
return false; 
} 
  
//重新获取数据 
$db->where(&#39;appid&#39;, $appid); 
$query = $db->get(TB_APPLOCK); 
  
if ($query === false) 
{ 
app_error_log("AppLock get fail.appid:$appid"); 
return false; 
} 
if (count($query->result_array()) <= 0) 
{ 
app_error_log("AppLock not data,appid:$appid"); 
return false; 
} 
} 
$applock_data = $query->row_array(); 
return $applock_data; 
} 
  
private function update($appid,$old_lock_mode,$new_lock_mode) 
{ 
$db = APP_DB(); 
  
$db->where(&#39;appid&#39;,$appid); 
$db->where(&#39;lock_mode&#39;,$old_lock_mode); 
  
$db->set(&#39;lock_mode&#39;,$new_lock_mode); 
$db->set(&#39;change_time&#39;,&#39;NOW()&#39;,false); 
  
$ret = $db->update(TB_APPLOCK); 
if ($ret === false) 
{ 
app_error_log("update applock error,appid:$appid,old_lock_mode:$old_lock_mode,new_lock_mode:$new_lock_mode"); 
return false; 
} 
return $db->affected_rows(); 
} 
  
//是否获取到了锁 
public $m_bGot = false; 
  
public $m_appid; 
}
Nach dem Login kopieren

为了防止死锁的问题,获取锁的逻辑中加入了超时时间的判断,大家看代码应该就能看懂

三. 对帐逻辑
如果按照上面的系统来设计,那么对帐的时候,只要对一下两边成功(即bill_ret=0)的流水即可,如果完全一致那么账户应该是没有问题的,如果不一致,那就要去查问题了。
关于保证账户正确性这里,也有同事跟我说,之前在公司做的时候,是采取只要有任何写操作之前,都先取一下流水表中所有的流水记录,将amt的值累加起来,看得到的结果是否和余额相同。如果不相同应该就是出问题了。
select sum(amt) from tb_bill where appid=1;
所以这也是为什么我在流水表中,amt字段是要区分正负的原因。

总结:以上就是本篇文的全部内容,希望能对大家的学习有所帮助。

相关推荐:

php实现获取当前月与上个月月初及月末时间戳的方法

php+ajax实现全选删除的方法

php+ajax无刷新上传图片的实现方法

Das obige ist der detaillierte Inhalt vonPHP-Zahlungssystemdesign und typische Fälle (empfohlen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

CakePHP-Projektkonfiguration CakePHP-Projektkonfiguration Sep 10, 2024 pm 05:25 PM

In diesem Kapitel werden wir die Umgebungsvariablen, die allgemeine Konfiguration, die Datenbankkonfiguration und die E-Mail-Konfiguration in CakePHP verstehen.

PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 bringt mehrere neue Funktionen, Sicherheitsverbesserungen und Leistungsverbesserungen mit einer beträchtlichen Menge an veralteten und entfernten Funktionen. In dieser Anleitung wird erklärt, wie Sie PHP 8.4 installieren oder auf PHP 8.4 auf Ubuntu, Debian oder deren Derivaten aktualisieren. Obwohl es möglich ist, PHP aus dem Quellcode zu kompilieren, ist die Installation aus einem APT-Repository wie unten erläutert oft schneller und sicherer, da diese Repositorys in Zukunft die neuesten Fehlerbehebungen und Sicherheitsupdates bereitstellen.

CakePHP Datum und Uhrzeit CakePHP Datum und Uhrzeit Sep 10, 2024 pm 05:27 PM

Um in cakephp4 mit Datum und Uhrzeit zu arbeiten, verwenden wir die verfügbare FrozenTime-Klasse.

CakePHP arbeitet mit Datenbank CakePHP arbeitet mit Datenbank Sep 10, 2024 pm 05:25 PM

Das Arbeiten mit der Datenbank in CakePHP ist sehr einfach. In diesem Kapitel werden wir die CRUD-Operationen (Erstellen, Lesen, Aktualisieren, Löschen) verstehen.

CakePHP-Datei hochladen CakePHP-Datei hochladen Sep 10, 2024 pm 05:27 PM

Um am Datei-Upload zu arbeiten, verwenden wir den Formular-Helfer. Hier ist ein Beispiel für den Datei-Upload.

CakePHP-Routing CakePHP-Routing Sep 10, 2024 pm 05:25 PM

In diesem Kapitel lernen wir die folgenden Themen im Zusammenhang mit dem Routing kennen.

Besprechen Sie CakePHP Besprechen Sie CakePHP Sep 10, 2024 pm 05:28 PM

CakePHP ist ein Open-Source-Framework für PHP. Es soll die Entwicklung, Bereitstellung und Wartung von Anwendungen erheblich vereinfachen. CakePHP basiert auf einer MVC-ähnlichen Architektur, die sowohl leistungsstark als auch leicht zu verstehen ist. Modelle, Ansichten und Controller gu

CakePHP erstellt Validatoren CakePHP erstellt Validatoren Sep 10, 2024 pm 05:26 PM

Der Validator kann durch Hinzufügen der folgenden zwei Zeilen im Controller erstellt werden.

See all articles