Commit c487b748 authored by wanjilong's avatar wanjilong

add: 提交创建支付订单模块

parent 72fb1170
......@@ -12,6 +12,7 @@ class PayException extends BaseException
protected $cus = [
0 => '用户已通过个人认证,无需重复认证',
1 => '企业已通过认证,无需重复认证'
1 => '企业已通过认证,无需重复认证',
4 => '费率计算错误,费率分母为0',
];
}
<?php
namespace Helpers;
/**
* Strategy 计算手续费策略
*/
class Strategy {
/**
* @param $current_amount
* @param $total_amount
* @param $cleared_amount
* @param $total_tip
* @param $cleared_tip
* 计算微信手续费公式,计算单位是分
*/
public static function getWxTip($current_amount, $total_amount, $cleared_amount, $total_tip, $cleared_tip) {
$left_amount = $total_amount - $cleared_amount;
if($left_amount == 0) {
return $left_amount;
}
return round($current_amount/$left_amount * ($total_tip - $cleared_tip));
}
/**
* @param $current_amount
* @param $total_amount
* @param $cleared_amount
* @param $total_tip
* @param $cleared_tip
* @return false|float
* 计算平台手续费
*/
public static function getTip($current_amount, $total_amount, $cleared_amount, $total_tip, $cleared_tip) {
$left_amount = $total_amount - $cleared_amount;
if($left_amount == 0) {
return $left_amount;
}
return round($current_amount/$left_amount * ($total_tip - $cleared_tip));
}
}
\ No newline at end of file
......@@ -24,4 +24,21 @@ class Dictionary
public const SOURCE_LIFE = 1; //生活圈
public const SOURCE_MAIN = 2; //主端
public const PAY_TYPE_IN = 1; //入账
public const PAY_TYPE_OUT = 2; //出账
public const PAY_SUB_TYPE_WX = 1; //微信手续费
public const PAY_SUB_TYPE_SYS_IN = 3; //平台营收
public const PAY_SUB_TYPE_M_IN = 4; //商家收入
public const PAY_SUB_TYPE_C_IN = 5; //团长收入
public const PAY_SUB_TYPE_SYS_OUT = 21; //平台补贴
public const PAY_SUB_TYPE_M_OUT = 22; //商家补贴
public const ACCOUNT_TYPE_C = 1; //用户
public const ACCOUNT_TYPE_M = 2; //商户
public const ACCOUNT_TYPE_SS = 3; //平台(平台收入+平台补贴)
public const ACCOUNT_TYPE_ST = 4; //平台交易总账+三方手续费
}
......@@ -21,10 +21,38 @@ class PayOrder extends MysqlBase
return 'pay_order_id';
}
public function selectForUpdate($id) {
/**
* @param $order_id
* @param $pay_order
* @return array
*/
public static function get_valid_order($order_id, $pay_order) {
$link = self::getConnection('write');
$link->pdo->
$order = [];
$link->action(function($link, $order_id, $pay_order) use (&$order) {
$_date = date('Y-h-m H:i:s');
$sql = "select * from " . self::getTableName() . " where order_id = :order_id and expire_time >:date limit 1 for update";
$order = $link->query(
$sql, [
":order_id" => $order_id,
":date" => $_date
]
)->fetch();
if(empty($order)) {
$new['order_id'] = $order_id;
$link->insert(self::getTableName(), $pay_order);
$order = $link->query(
$sql, [
":order_id" => $order_id,
":date" => $_date
]
)->fetch();
}
});
return $order;
}
}
......@@ -25,7 +25,7 @@ class AccountController extends Base
/**
* 创建未认证的个人生活号
*/
public function create_unauthorizedAction()
public function createAction()
{
// 获取手机号
(new UserPhoneValidate())->validate();
......
......@@ -55,6 +55,9 @@ class OrderService
]
];
foreach ($order_items as $r) {
$order_distribution[] = [
'commission_mode'=>1,
......
......@@ -5,23 +5,43 @@ namespace App\Services\pay;
use Api\PhpServices\Idgen\Idgen;
use App\Exception\custom\PayException;
use App\Models\Dictionary;
use App\Models\order\mysql\PayOrder;
use App\Models\order\mysql\PayOrderClearing;
use App\Models\order\mysql\PayOrderClearingItem;
use App\Services\Order\OrderService;
use Helpers\Strategy;
use Helpers\Logger;
use App\Exception\custom\PayException;
class PayService
{
// 订单信息
private $order_info = [];
// 子订单信息
private $order_items = [];
// 分销信息
private $order_distribution = [];
public function do_pay($order_id, $user_id) {
private $pay_order = [];
private $clear_list = [];
private $clear_items_list = [];
// 判断是否存在有效订单
$this->exist_valid($order_id, $user_id);
public function do_pay($order_id, $user_id) {
// 获取订单信息 + 子单信息 + 分润信息
$data = $this->get_order_data($order_id, $user_id);
$data = OrderService::getOrderData($order_id, $user_id);
if(empty($data['order'])) {
throw new PayException(['cus' => 1]);
}
// 创建订单信息
$pay_order = $this->make_pay_order($data);
// 判断是否存在有效订单
$pay_order = $this->exist_valid($order_id, $data['order']);
$this->make_order_clearing($data);
// 发送ping++ 创建支付订单
$this->send_pingxx($pay_order);
......@@ -33,118 +53,358 @@ class PayService
* @throws PayException
* 判断是否有效订单,如果存在则调用三方判断订单状态
*/
private function exist_valid($order_id, $user_id) {
$cur_date = date('Y-m-d H:i:s');
$order = PayOrder::ge(["order_id" => $order_id, 'expire_time[>]'=>$cur_date]);
if(!empty($order)) {
if($order['user_id'] != $user_id) {
throw new PayException(['cus' => 1]);
}
private function exist_valid($order_id, $order) {
$expire_time = time() + 30 * 60;
$pay_order_id = $this->gen_pay_order_id($order_id);
$pay_order = [
'pay_order_id'=>$pay_order_id,
'user_id'=>$order['user_id'],
'life_account_id'=>$order['life_account_id'],
'shop_id'=>$order['shop_id'],
'order_id'=>$order['order_id'],
'pay_order_status'=>Dictionary::O_PAY_STATUS_INIT,
'pay_amount'=>$order['payment'],
'third_order_id'=>'',
'expire_time'=>$expire_time,
'is_distribution'=>$order['is_distribution'] ?? 0,
'source_name'=>1,
'service_name'=>1,
'extra'=> json_encode([]),
];
$order = PayOrder::get_valid_order($order_id, $pay_order);
if(empty($order)) {
throw new PayException(['cus' => 2]);
//$this->get_order_from_pingxx($order['third_order_id']);
}
if(empty($order['third_order_id'])) {
throw new PayException(['cus' => 2]);
}
//新创建直接返回
if($order['pay_order_id'] == $pay_order_id) {
return $order;
}
//需要核对ping++ 支付状态
if(!empty($order['third_order_id'])) {
$this->get_order_from_pingxx($order['third_order_id']);
throw new PayException(['cus' => 3]);
}
}
private function get_order_data($order_id, $user_id) {
return $order;
}
/**
* @param $data
* 构造订单流水
*/
private function make_order_clearing($data) {
$this->order_info = $data['order'];
$this->order_items = $data['order_items'];
$this->order_distribution = $data['order_distribution'];
public function makePayOrder($data) {
$this->clear_list = [];
$this->clear_items_list = [];
}
$wx_tip = $this->make_tip_clearing(); //微信手续费
$subsidy_tip = $this->make_platform_subsidy_clearing(); //系统补贴
$merchant_tip = $this->make_merchant_subsidy_clearing(); //商家补贴
public function getInfo($orderId) {
$distribution_tip = $this->make_distribution_clearing(); //团长收益
$platform_tip = $this->make_platform_clearing(); //平台收益
$order = PayOrder::getRecord(["order_id" => $orderId]);
$merchant_cash = $this->order_info['payment'] + $subsidy_tip + $merchant_tip - $wx_tip - $distribution_tip - $platform_tip;
$this->make_merchant_clearing($merchant_cash); //商户收益
`expire_time` timestamp NULL DEFAULT NULL COMMENT '支付订单过期时间',
pay_order_status
try{
PayOrderClearing::beginTransaction();
$cnt = PayOrderClearing::insert($this->clear_list, ['rowCount'=>true]);
$items_cnt = PayOrderClearingItem::insert($this->clear_items_list, ['rowCount'=>true]);
if($cnt >0 || $items_cnt>0) {
PayOrderClearing::commit();
}
}catch (\PDOException $e) {
Logger::error($e->getMessage(), []);
PayOrderClearing::rollback();
throw new PayException(['cut'=>5]);
}
}
/**
* @param $payment
* @return false|float
* 获取微信手续费
*/
private function make_tip_clearing() {
$total_tip = round(0.006 * $this->order_info['payment']);
$account = [
'account_id'=>'100020003001',
'amount'=>$total_tip,
'title'=>'微信手续费',
];
if(empty($total_tip)) {
return 0;
}
$account['account_type'] = Dictionary::ACCOUNT_TYPE_ST;
$account['pay_type'] = Dictionary::PAY_TYPE_IN;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_WX;
$account['need_recorded'] = Dictionary::NO;
/* 微信手续费总单,在退款、核销的时候会更新这个金额,保证他的准确 */
$this->do_clearing_data($account, $total_tip, true);
return $total_tip;
}
public function iPingCreate() {
/**
* 计算平台运营补贴
*/
private function make_platform_subsidy_clearing() {
$account = $this->get_marketing_subsidy();
$total_tip = $account['amount'];
if(empty($total_tip)) {
return 0;
}
$account['account_type'] = Dictionary::ACCOUNT_TYPE_SS;
$account['pay_type'] = Dictionary::PAY_TYPE_OUT;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_SYS_OUT;
$account['need_recorded'] = Dictionary::NO;
$this->do_clearing_data($account, $total_tip, false);
return $total_tip;
}
/**
* 创建店铺,同时更新生活号表的店铺数
* @param $life_account_id
* @param $merchant_id
* @param string $name
* @param bool $audit 是否需要审核
* @return \Api\PhpUtils\Mysql\MysqlBase
* @throws LifeAccountException
* 计算商户运营补贴
*/
public function create($life_account_id, $merchant_id, $name='', $shop_status=Shop::SHOP_STATUS_ON){
private function make_merchant_subsidy_clearing() {
$account = $this->get_marketing_subsidy();
$total_tip = $account['amount'];
if(empty($total_tip)) {
return 0;
}
$account['account_type'] = Dictionary::ACCOUNT_TYPE_M;
$account['pay_type'] = Dictionary::PAY_TYPE_OUT;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_M_OUT;
$account['need_recorded'] = Dictionary::NO;
$life = LifeAccount::getMaster(['life_account_name'],['life_account_id' => $life_account_id]);
if(empty($life)){
throw new LifeAccountException(['cus'=>2]);
}
$this->do_clearing_data($account, $total_tip, false);
return $total_tip;
}
// 店铺id,通过idgen生成
$shop_id = OrderService::get_idgen_id(substr($life_account_id, -2));
$shop_id = $shop_id[0] ?? '';
if(empty($shop_id)){
throw new ShopException(['cus'=>1]);
/**
* @param $total
* @return false|float
* 计算平台收入
*/
private function make_platform_clearing() {
$account = [
'account_id'=>'100020003201',
'amount'=>0,
'rate'=>60,
'title'=>'平台收益',
];
$total_tip = $account['amount'] = round($this->order_info['total_price'] * $account['rate'] / 10000);
if(empty($total_tip)) {
return 0;
}
// 店铺名,默认同生活号名
if(empty($name)){
$name = $life['life_account_name'];
$account['account_type'] = Dictionary::ACCOUNT_TYPE_SS;
$account['pay_type'] = Dictionary::PAY_TYPE_IN;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_SYS_IN;
$account['need_recorded'] = Dictionary::NO;
$this->do_clearing_data($account, $total_tip, false);
return $total_tip;
}
/**
* @return mixed
* 计算分销信息
*/
private function make_distribution_clearing() {
if($this->order_info['is_distribution'] == 0) {
return false;
}
$shop_res = Shop::insertRecord([
'shop_id' => $shop_id,
'shop_name' => $name,
'merchant_id' => $merchant_id,
'life_account_id' => $life_account_id,
'shop_status' => $shop_status
]);
/**
'distributor_user_id'=>122234,
'distributor_commission_value'=>60,
'parent_user_id'=>122234 . rand(111, 999),
'parent_commission_value'=>60,
*/
$list = [
[
'user_id'=>$this->order_distribution['distributor_user_id'],
'commission_value'=>$this->order_distribution['distributor_commission_value'],
'commission_type'=>$this->order_distribution['distributor_commission_type'],
],
[
'user_id'=>$this->order_distribution['parent_user_id'],
'commission_value'=>$this->order_distribution['parent_commission_value'],
'commission_type'=>$this->order_distribution['parent_commission_type'],
],
];
$total_tip = 0;
foreach ($list as $account) {
if($account['commission_type'] == 1) {//rate
$account['amount'] = round($account['commission_value'] * $this->order_info['total_price'] / 10000);
} else { //定值
$account['amount'] = $account['commission_value'];
}
$account['account_id'] = $account['account_id'];
$account['account_type'] = Dictionary::ACCOUNT_TYPE_M;
$account['pay_type'] = Dictionary::PAY_TYPE_OUT;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_M_OUT;
$account['need_recorded'] = Dictionary::NO;
if($shop_res===false){
throw new ShopException(['cus'=>0]);
$this->do_clearing_data($account, $account['amount'], false);
$total_tip += $account['amount'];
}
// 更新生活号店铺数
$shop_res2 = LifeAccount::update([
"shop_totel_count[+]" => 1,
"shop_count[+]" => 1,
],[
"life_account_id" => $life_account_id
]);
return $total_tip;
}
/**
* @param $merchant_cash
* @return mixed
* 计算商户清分信息
*/
private function make_merchant_clearing($merchant_cash) {
if(!$shop_res2){
throw new ShopException(['cus'=>2]);
$account = [
'account_id'=>$this->order_info['life_account_id'],
'amount'=>$merchant_cash,
'title'=>'商家订单收益',
];
$account['account_type'] = Dictionary::ACCOUNT_TYPE_M;
$account['pay_type'] = Dictionary::PAY_TYPE_IN;
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_M_IN;
$account['need_recorded'] = Dictionary::NO;
$this->do_clearing_data($account, $merchant_cash, false);
return $merchant_cash;
}
private function do_clearing_data($account, $total_tip, $is_wx = false) {
$total_amount = $this->order_info['payment']; //订单支付总价格
$cleared_amount = 0; //已经结算记录
$cleared_tip = 0; //已经结算记录
$pay_order_clearing_id = $this->gen_pay_order_clearing_id();
$this->clear_list[] = [
'pay_order_clearing_id' => $pay_order_clearing_id,
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->order_info['order_id'],
'account_id' => $account['account_id'],
'account_type' => $account['account_type'],
'pay_amount' => $account['amount'],
'pay_type' => $account['pay_type'],
'pay_sub_type' => $account['pay_sub_type'],
'need_recorded' => $account['need_recorded'],
];
foreach ($this->order_items as $r) {
if($is_wx) {
$current_tip = Strategy::getWxTip($r['payment'], $total_amount, $cleared_amount, $total_tip, $cleared_tip);
} else {
$current_tip = Strategy::getTip($r['payment'], $total_amount, $cleared_amount, $total_tip, $cleared_tip);
}
$this->clear_items_list[] = [
'pay_order_clearing_item_id' => $this->gen_pay_order_clearing_item_id(),
'pay_order_clearing_id' => $this->pay_order['pay_order_id'],
'pay_order_id' => $this->order_info['order_id'],
'order_id' => $this->order_info['order_id'],
'account_id' => $account['account_id'],
'account_type' => $account['account_type'],
'pay_amount' => $current_tip,
'pay_type' => $account['pay_type'],
'pay_sub_type' => $account['pay_sub_type'],
'need_recorded' => $account['need_recorded'],
];
$cleared_amount += $r['payment'];
$cleared_tip += $current_tip;
}
}
return $shop_id;
/**
* @param $order_id
* @param $user_id
* @return string[]
* 获取支付订单的营销补贴,返回补贴出款账号,补贴金额,补贴原因
*/
private function get_marketing_subsidy() {
return [
'account_id'=>'100020003001',
'amount'=>1000,
'title'=>'营销专号支付补贴',
];
}
/**
* 更新商户id
* @param $order
* @return string[]
* 获取订单优惠券补贴,一般来自订单,需要这里完善出款账号 + 原因
*/
public static function changeMid($o_mid, $n_mid){
$res = Shop::update([
'merchant_id' => $n_mid
],[
'merchant_id' => $o_mid
]);
if($res===false){
throw new ShopException(['cus'=>3]);
}
return true;
private function get_coupon_subsidy($order) {
return [
'account_id'=>'100020002001',
'amount'=>'0',
'title'=>'营销专号支付补贴',
];
}
private function gen_pay_order_id($order_id) {
$number = $order_id;
return $this->get_idgen_id($number, 1);
}
private function gen_pay_order_clearing_id() {
$number = $this->order_info['order_id'];
return $this->get_idgen_id($number, 1);
}
private function gen_pay_order_clearing_item_id() {
$number = $this->order_info['order_id'];
return $this->get_idgen_id($number, 1);
}
private function get_idgen_id($number, $count=1){
$res = Idgen::get(appConfig('idgen.partner'),appConfig('idgen.key'), [], [[
"type" => "merchant_shop_id",
'number'=>$number,
"count"=> $count]]);
$id = $res['id_datetime']['merchant_shop_id'] ?? [];
return $id;
}
/**
* @param $data
* 构造订单支付订单信息
*/
private function make_new_pay($data) {
$this->make_new_pay_order($data['order']);
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
"type": "project",
"license": "MIT",
"require": {
"php": "7.4.*",
"php": "7.2.*",
"ext-json": "*",
"api/php_utils":"dev-master",
"api/php_services":"dev-master",
......@@ -41,4 +41,4 @@
"url":"https://gitlab.yidian-inc.com/bp/php_services.git"
}
}
}
\ No newline at end of file
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment