PHP 결제 시스템 디자인 과 전형 적 인 사례 공유

10544 단어 PHP지불 하 다.
회사 의 업무 수요 로 인해 2 주 동안 작은 결제 시스템 을 실현 했다.참 새 는 작 지만 오장 이 모두 갖 추어 져 있 고 각종 필수 적 인 모듈,예 를 들 어 계좌 잠 금,사무 성 보증,흐름 과 장부 대조 등 이 모두 완전 하 게 실현 되 었 다.전체 개발 과정 에서 많은 경험 을 쌓 았 다.게다가 인터넷 에서 검색 한 결과 대부분이 연구 성 논문 이다.실제 사용 에 대한 가치 가 크 지 않 기 때문에 이번 에는 특별히 꺼 내 서 여러분 과 공유 하 겠 습 니 다.
이 시스템 은 소형 결제 시스템 으로 도 사용 할 수 있 고 제3자 응용 으로 개방 플랫폼 에 접속 할 때의 결제 흐름 시스템 으로 도 사용 할 수 있다.
원래 의 수요 가 비교적 책임감 이 있 기 때문에 나 는 간단하게 말 했다.
모든 응용 프로그램 에 대해 대외 적 으로 잔액 획득,지불 설비,충전 등 인 터 페 이 스 를 제공 해 야 한다.
백 스테이지 에 절차 가 있어 서 매달 1 일 에 청산 을 한다.
계 정 이 동 결 될 수 있 습 니 다.
매번 작업 의 흐름 을 기록 해 야 하 며,매일 흐 르 는 물 은 발기인 과 장 부 를 대조 해 야 한다.
위의 수요 에 따라 우 리 는 다음 과 같은 데이터 베 이 스 를 설정 합 니 다.

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());
상세 한 설명 은 다음 과 같다.
tb_status 응용 상태 표.계 정 이 동결 되 었 는 지,계 정의 유형 이 무엇 인지 책임 집 니 다.(실제 수 요 는 두 가지 계 정 이 있 을 수 있 습 니 다.여 기 는 간단 하기 때문에 열거 되 지 않 았 습 니 다)
appid 응용 id
동결 여부
create_시간 생 성 시간
change_time 마지막 수정 시간
tb_account_earn 응용 계 정 잔액 표
appid 응용 id
balance 잔액(단 위 는 점수 이 고 소수 로 저장 하지 마 세 요.소수 자체 가 정확 하지 않 기 때 문 입 니 다.또한 php 는 64 비트 컴퓨터 에서 만 bigint 를 지원 할 수 있 습 니 다)
create_시간 생 성 시간
change_time 마지막 수정 시간
seqid 작업 시퀀스 번호(동시 다발 방지,매번 update+1)
tb_assign 분배 유수 id 의 시계,tbbill 의 billid 는 tb 가 있어 야 합 니 다.할당
id 자체 증가 id
create_시간 생 성 시간
tb_bill 유량계.모든 작업 흐름 을 기록 하 는 일 을 맡 고 있 습 니 다.여기 빌id 는 메 인 키 가 아 닙 니 다.같은 bill 때 문 입 니 다.id 지불 과 스크롤 백 두 개의 흐름 이 있 을 수 있 습 니 다.
id 자체 증가 시퀀스 번호
bill_id 흐름 번호
amt 작업 금액(이것 은 양음 을 구별 해 야 하 는데 주로 selectall 을 위해 특정한 시간의 금액 변 화 를 직접 계산 할 수 있 습 니 다)
bill_info 작업 에 대한 자세 한 정보,예 를 들 어 웹 서버 3 대,db 2 대
bill_사용자 조작 사용자
bill_시간 흐름 시간
bill_type 흐름 유형
bill_channel 흐름 원,예 를 들 어 충전,지불,스크롤 백,결제 또는 기타
bill_ret 흐름 의 반환 코드 는 처리 되 지 않 음,성공,실 패 를 포함 하고 여기 의 논 리 는 뒤에서 설명 할 것 입 니 다.
appid 응용 id
old_balance 작업 발생 전 계좌 잔액
price_info 작업 발생 시 지불 되 는 물품 의 단 가 를 기록 합 니 다.
src_ip 클 라 이언 트 ip
tb_price 단가 표,기계 단가 기록
name 기계 유일한 표식
가격
정보 설명
tb_applock 잠 금 표 입 니 다.이것 은 특정한 응용 프로그램 에 대해 작성 작업 디자인 을 하 는 것 을 피하 기 위해 서 입 니 다.구체 적 인 코드 는 뒤에서 보 여 줍 니 다.
appid 응용 id
lock_mode 잠 금 상태.0 이면 잠 금,1 이면 잠 금.
change_time 마지막 수정 시간
OK,라 이브 러 리 가 디자인 된 후에 우 리 는 가장 전형 적 인 몇 가지 조작 을 살 펴 보 겠 습 니 다.
지불 조작
나 는 지금 내 가 실현 하고 있 는 방식 만 열거 했다.아마도 가장 좋 은 것 은 아 닐 것 이다.그러나 가장 경제적 이 고 수 요 를 만족 시 켜 야 한다.
먼저 호출 자 에 게 논 리 는 다음 과 같다.

그 다음 에 대응 하 는 결제 시스템 내부 논 리 는 다음 과 같다.

자주 사용 하 는 오류 반환 코드 는 다음 과 같 으 면 충분 할 것 입 니 다.

$g_site_error = array( 
-1 => '     ', 
-2 => '       ', 
-3 => '       ', 
  
0 => '  ', 
  
1 => '    ', 
2 => '    ', 
3 => '    ', 
4 => '     ', 
5 => '     ', 
6 => '    ', 
);
0 이상 의 오 류 는 모두 논리 적 오류 라 고 할 수 있 고 지불 작업 을 수행 할 때 호출 자 는 물 흐름 을 기록 하지 않 아 도 된다.계 정 이 바 뀌 지 않 았 기 때문이다.
0 이하 의 오 류 는 시스템 내부 오류 입 니 다.데이터 변경 이 발생 했 는 지 모 르 기 때문에 호출 자 와 지불 시스템 은 모두 흐름 을 기록 해 야 합 니 다.
0 과 같은 귀환 은 성공 을 의미 하 며 양쪽 에 도 흐 르 는 물 을 기록 해 야 한다.
결제 시스템 내부 에서 먼저 흐름 을 기록 한 다음 에 계 정 을 업데이트 하 는 방식 을 사용 하 는 것 도 이유 가 있다.쉽게 말 하면 흐름 을 잃 지 않도록 하 는 것 이다.
마지막 으로 정리 해 보면 이런 돈 을 먼저 공제 하고 화물 을 발송 하 며 문제 가 생기 면 다시 굴 러 가 는 방식 은 일종 의 모델 이다.또 하 나 는 미리 공제 하고 나중에 발송 하 며 문제 가 생기 지 않 으 면 지불 확인 을 통 해 공제 하고 문제 가 생기 면 지불 스크롤 백 을 통 해 취소 하 며,미리 공제 한 후 오랫동안 아무런 확인 도 하지 않 으 면 금액 이 자동 으로 굴 러 간다.
2.계 정 잠 금 의 실현
여기 서 데이터 뱅 크 의 잠 금 체 제 를 이용 하여 구체 적 인 논 리 는 말 하지 않 겠 습 니 다.코드 는 다음 과 같 습 니 다.

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('appid', $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( 
'appid'=>$appid, 
'lock_mode'=>APPLOCK_MODE_FREE, 
); 
$db->set('change_time','NOW()',false); 
$ret = $db->insert(TB_APPLOCK, $applock_data); 
if ($ret === false) 
{ 
app_error_log("applock insert fail:$appid"); 
return false; 
} 
  
//       
$db->where('appid', $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('appid',$appid); 
$db->where('lock_mode',$old_lock_mode); 
  
$db->set('lock_mode',$new_lock_mode); 
$db->set('change_time','NOW()',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; 
}
잠 금 문 제 를 방지 하기 위해 잠 금 을 가 져 오 는 논리 에 시간 초과 판단 을 넣 었 으 니 코드 를 보면 알 수 있 을 것 입 니 다.
회계 논리
위의 시스템 에 따라 설계 하면 장 부 를 맞 출 때 양쪽 을 맞 추 면 성공 합 니 다(즉 빌ret=0)의 흐 르 는 물 만 있 으 면 됩 니 다.완전히 일치 하면 계 정 은 문제 가 없 을 것 입 니 다.일치 하지 않 으 면 문 제 를 찾 아야 합 니 다.
계좌 의 정확성 을 확보 하 는 데 있어 서 동료 들 도 저 에 게 예전 에 회사 에서 했 을 때 어떤 조작 이 있 으 면 먼저 흐름 표 에 있 는 모든 흐름 기록 을 취하 고 amt 의 가 치 를 누적 하여 얻 은 결과 가 잔액 과 같 는 지 확인 하 라 고 말 했 습 니 다.다 르 면 문제 가 생 긴 것 같 습 니 다.
select sum(amt) from tb_bill where appid=1;
그래서 이것 도 내 가 흐름 표 에서 amt 필드 는 플러스 와 마이너스 를 구분 해 야 하 는 이유 이다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기