Commit d4916818 authored by wanjilong's avatar wanjilong

add: init pay_v2

parent dc61a339
...@@ -15,4 +15,6 @@ class Code ...@@ -15,4 +15,6 @@ class Code
const REFUND = 102000; const REFUND = 102000;
const ORDER = 103000; const ORDER = 103000;
const SDK = 104000; const SDK = 104000;
const FUNDS = 105000;
const P_ACCOUNT = 106000;
} }
\ No newline at end of file
...@@ -6,28 +6,12 @@ namespace App\Exception\custom; ...@@ -6,28 +6,12 @@ namespace App\Exception\custom;
use App\Exception\BaseException; use App\Exception\BaseException;
class RefundException extends BaseException class FundsException extends BaseException
{ {
protected $base_code = Code::REFUND; protected $base_code = Code::FUNDS;
protected $cus = [ protected $cus = [
0 => '系统异常请联系管理员', 0 => '系统异常请联系管理员',
1 => '缺少退款订单ID!', 1 => '缺少渠道平台账户配置,请联系管理员!',
2 => '获取订单信息返回错误,请核对',
3 => '仅支持自己操作订单退款!',
4 => '目前仅支持支付后的订单退款,请核对',
5 => '已核销计算订单不支持退款!',
6 => '退款处理完成,请勿重复申请',
7 => '退款处理中请稍等',
8 => '退款消息类型匹配失败,请管理员关注!',
9 => 'pingxx退款消息格式错误,请管理员关注!',
10 => 'pingxx退单状态异常,请管理员关注!',
11 => 'pingxx退单缺少matadata!',
12 => 'pingxx未支付订单不允许退款。',
13 => 'pingxx订单可能存在多次成功支付,需人工确认处理。',
14 => '不存在满足条件的退款订单。',
15 => '订单未支付或不存在,请核对。',
16 => '订单退款回调处理失败,请核对。',
]; ];
} }
...@@ -13,24 +13,34 @@ class PayException extends BaseException ...@@ -13,24 +13,34 @@ class PayException extends BaseException
protected $cus = [ protected $cus = [
0 => '系统错误,请稍收重试', 0 => '系统错误,请稍收重试',
1 => '请输入订单ID', 1 => '请输入订单ID',
2 => '订单服务获取订单信息失败。', 2 => '请输入用户ID',
3 => '请确认订单信息及用户是否一致。', 3 => '订单服务获取订单信息失败。',
4 => '支付订单创建失败,请管理员关注', 4 => '请确认订单信息及用户是否一致。',
5 => '该订单已经支付,请稍后刷新', 5 => '订单缺少过期时间,请联系管理员',
6 => '该订单已超时取消关闭,请重新下单购买', 6 => '订单过期,不允许支付',
7 => '支付订单创建失败,请管理员关注',
7 => '支付回调订单不存在,请管理员关注', 8 => '支付订单创建失败,请管理员关注',
8 => '支付订单金额与实际支付金额不等,请管理员关注', 9 => '订单已经支付,请勿重复支付',
9 => '退款中订单不允许核销,请管理员关注',
10 => '回调失败,支付订单不存在',
11 => '回调失败,支付金额错误',
12 => '订单服务优惠信息错误',
13 => '订单明细保存失败',
10 => '未支付订单不允许核销,请管理员关注', 10 => '未支付订单不允许核销,请管理员关注',
11 => '商家仅允许核销自己的订单,请管理员关注', 11 => '商家仅允许核销自己的订单,请管理员关注',
12 => '支付订单预结算失败,结算资金不能负数,请核对配置', 12 => '支付订单预结算失败,结算资金不能负数,请核对配置',
13 => '生活号获取管理员ID失败,请联系管理员', 13 => '生活号获取管理员ID失败,请联系管理员',
14 => '订单缺少商品信息,请联系管理员', 14 => '订单缺少商品信息,请联系管理员',
15 => '订单存在过期商品,不能支付', 15 => '订单存在过期商品,不能支付',
16 => '订单过期,不允许支付',
17 => '订单缺少过期时间,请联系管理员',
18 => '订单不存在,请重试', 18 => '订单不存在,请重试',
100=>'支付渠道错误,暂不支持该渠道',
101=>'该订单已经支付,请稍后刷新',
102=>'该订单已超时取消关闭,请重新下单购买',
103=>'该订单已超时取消关闭,请重新下单购买',
104=>'请增加pingxx渠道手续费配置',
]; ];
} }
...@@ -6,9 +6,9 @@ namespace App\Exception\custom; ...@@ -6,9 +6,9 @@ namespace App\Exception\custom;
use App\Exception\BaseException; use App\Exception\BaseException;
class FundsException extends BaseException class PlatformAccountException extends BaseException
{ {
protected $base_code = Code::FUNDS; protected $base_code = Code::P_ACCOUNT;
protected $cus = [ protected $cus = [
0 => '系统异常请联系管理员', 0 => '系统异常请联系管理员',
......
...@@ -11,23 +11,16 @@ class RefundException extends BaseException ...@@ -11,23 +11,16 @@ class RefundException extends BaseException
protected $base_code = Code::REFUND; protected $base_code = Code::REFUND;
protected $cus = [ protected $cus = [
0 => '系统异常请联系管理员', 0 => '系统异常请联系管理员',
1 => '缺少退款订单ID!', 1 => '缺少退款订单ID!',
2 => '获取订单信息返回错误,请核对', 2 => '订单未支付或不存在,请核对。',
3 => '仅支持自己操作订单退款!', 3 => '不存在满足条件的退款订单。',
4 => '目前仅支持支付后的订单退款,请核对', 4 => '退单保存失败,请联系管理员。',
5 => '已核销计算订单不支持退款!', 5 => '订单退款回调处理失败,请核对。',
6 => '退款处理完成,请勿重复申请',
7 => '退款处理中请稍等', 6 => 'pingxx退单缺少matadata!',
7 => 'pingxx未支付订单不允许退款。',
8 => '退款消息类型匹配失败,请管理员关注!', 8 => 'pingxx订单可能存在多次成功支付,需人工确认处理。',
9 => 'pingxx退款消息格式错误,请管理员关注!',
10 => 'pingxx退单状态异常,请管理员关注!',
11 => 'pingxx退单缺少matadata!',
12 => 'pingxx未支付订单不允许退款。',
13 => 'pingxx订单可能存在多次成功支付,需人工确认处理。',
14 => '不存在满足条件的退款订单。',
15 => '订单未支付或不存在,请核对。',
16 => '订单退款回调处理失败,请核对。',
]; ];
} }
...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase; ...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase;
class CallbackLog extends MysqlBase class CallbackLog extends MysqlBase
{ {
const TABLE_NAME = 'callback_log'; const TABLE_NAME = 'callback_log';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql; ...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
use App\Exception\custom\PayException; use App\Exception\custom\PayException;
class PayOrder extends MysqlBase class MarketingAccount extends MysqlBase
{ {
const TABLE_NAME = 'pay_order'; const TABLE_NAME = 'marketing_account';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
/**
* @param $order_id
* @param $pay_order
* @return array
*/
public static function get_valid_order($order_id, $pay_order) {
self::beginTransaction();
try{
$order = self::getMaster('*', ['order_id'=>$order_id]);
if(empty($order)) {
$cnt = self::insert($pay_order, ['rowCount'=>true]);
if($cnt) {
$order = $pay_order;
}
}
self::commit();
}catch (\PDOException $e) {
self::rollback();
}
return $order;
}
} }
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
namespace App\Models\order\mysql; namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
use App\Exception\custom\PayException;
class MarketingAccount extends MysqlBase class MarketingAccountLog extends MysqlBase
{ {
const TABLE_NAME = 'marketing_account'; const TABLE_NAME = 'marketing_account_log';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class MarketingAccountLog extends MysqlBase class MarketingAccountMap extends MysqlBase
{ {
const TABLE_NAME = 'marketing_account_log'; const TABLE_NAME = 'marketing_account_map';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql; ...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
use App\Exception\custom\PayException; use App\Exception\custom\PayException;
class PayOrder extends MysqlBase class PayCouponItem extends MysqlBase
{ {
const TABLE_NAME = 'pay_order'; const TABLE_NAME = 'pay_coupon_item';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
/**
* @param $order_id
* @param $pay_order
* @return array
*/
public static function get_valid_order($order_id, $pay_order) {
self::beginTransaction();
try{
$order = self::getMaster('*', ['order_id'=>$order_id]);
if(empty($order)) {
$cnt = self::insert($pay_order, ['rowCount'=>true]);
if($cnt) {
$order = $pay_order;
}
}
self::commit();
}catch (\PDOException $e) {
self::rollback();
}
return $order;
}
} }
...@@ -7,7 +7,7 @@ use App\Exception\custom\PayException; ...@@ -7,7 +7,7 @@ use App\Exception\custom\PayException;
class PayOrder extends MysqlBase class PayOrder extends MysqlBase
{ {
const TABLE_NAME = 'pay_order'; const TABLE_NAME = 'pay_order';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
/** /**
* @param $order_id * @param $order_id
......
...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase; ...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase;
class PayOrderClearing extends MysqlBase class PayOrderClearing extends MysqlBase
{ {
const TABLE_NAME = 'pay_order_clearing'; const TABLE_NAME = 'pay_order_clearing';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -7,7 +7,7 @@ use App\Exception\BaseException; ...@@ -7,7 +7,7 @@ use App\Exception\BaseException;
class PayOrderClearingItem extends MysqlBase class PayOrderClearingItem extends MysqlBase
{ {
const TABLE_NAME = 'pay_order_clearing_item'; const TABLE_NAME = 'pay_order_clearing_item';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
public static function marketSaleCount($accountId) { public static function marketSaleCount($accountId) {
......
...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase; ...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase;
class PayOrderItem extends MysqlBase class PayOrderItem extends MysqlBase
{ {
const TABLE_NAME = 'pay_order_item'; const TABLE_NAME = 'pay_order_item';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql; ...@@ -4,34 +4,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
use App\Exception\custom\PayException; use App\Exception\custom\PayException;
class PayOrder extends MysqlBase class PayOrderLog extends MysqlBase
{ {
const TABLE_NAME = 'pay_order'; const TABLE_NAME = 'pay_order_log';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
/**
* @param $order_id
* @param $pay_order
* @return array
*/
public static function get_valid_order($order_id, $pay_order) {
self::beginTransaction();
try{
$order = self::getMaster('*', ['order_id'=>$order_id]);
if(empty($order)) {
$cnt = self::insert($pay_order, ['rowCount'=>true]);
if($cnt) {
$order = $pay_order;
}
}
self::commit();
}catch (\PDOException $e) {
self::rollback();
}
return $order;
}
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class PlatformFunds extends MysqlBase class PlatformAccount extends MysqlBase
{ {
const TABLE_NAME = 'platform_funds_log'; const TABLE_NAME = 'platform_account';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class PlatformAccount extends MysqlBase class PlatformAccountLog extends MysqlBase
{ {
const TABLE_NAME = 'platform_account'; const TABLE_NAME = 'platform_account_log';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class PlatformFunds extends MysqlBase class PlatformConfig extends MysqlBase
{ {
const TABLE_NAME = 'platform_funds_log'; const TABLE_NAME = 'platform_config';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class PlatformAccount extends MysqlBase class PlatformFunds extends MysqlBase
{ {
const TABLE_NAME = 'platform_account'; const TABLE_NAME = 'platform_funds';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class PlatformAccountLog extends MysqlBase class PlatformFundsLog extends MysqlBase
{ {
const TABLE_NAME = 'platform_account_log'; const TABLE_NAME = 'platform_funds_log';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundPlatformRecord extends MysqlBase class RefundMerchantRecord extends MysqlBase
{ {
const TABLE_NAME = 'refund_platform_record'; const TABLE_NAME = 'refund_merchant_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase; ...@@ -6,5 +6,5 @@ use Api\PhpUtils\Mysql\MysqlBase;
class RefundOrder extends MysqlBase class RefundOrder extends MysqlBase
{ {
const TABLE_NAME = 'refund_order'; const TABLE_NAME = 'refund_order';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundOrder extends MysqlBase class RefundOrderItem extends MysqlBase
{ {
const TABLE_NAME = 'refund_order'; const TABLE_NAME = 'refund_order_item';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundOrderItem extends MysqlBase class RefundPersonalRecord extends MysqlBase
{ {
const TABLE_NAME = 'refund_order_item'; const TABLE_NAME = 'refund_personal_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundPersonalRecord extends MysqlBase class RefundPlatformRecord extends MysqlBase
{ {
const TABLE_NAME = 'refund_personal_record'; const TABLE_NAME = 'refund_platform_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class WriteOffOrder extends MysqlBase class WriteOffMerchantRecord extends MysqlBase
{ {
const TABLE_NAME = 'writeoff_order'; const TABLE_NAME = 'writeoff_merchant_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundOrder extends MysqlBase class WriteOffOrder extends MysqlBase
{ {
const TABLE_NAME = 'refund_order'; const TABLE_NAME = 'writeoff_order';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class RefundOrder extends MysqlBase class WriteOffOrderItem extends MysqlBase
{ {
const TABLE_NAME = 'refund_order'; const TABLE_NAME = 'writeoff_order_item';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class WriteOffMerchantRecord extends MysqlBase class WriteOffPersonalRecord extends MysqlBase
{ {
const TABLE_NAME = 'writeoff_merchant_record'; const TABLE_NAME = 'writeoff_personal_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql; ...@@ -3,8 +3,8 @@ namespace App\Models\order\mysql;
use Api\PhpUtils\Mysql\MysqlBase; use Api\PhpUtils\Mysql\MysqlBase;
class WriteOffPersonalRecord extends MysqlBase class WriteOffPlatformRecord extends MysqlBase
{ {
const TABLE_NAME = 'writeoff_personal_record'; const TABLE_NAME = 'writeoff_platform_record';
const CONFIG_INDEX = 'pay'; const CONFIG_INDEX = 'pay_v2';
} }
<?php <?php
use Api\PhpServices\Idgen\Idgen;
use App\Base\Base; use App\Base\Base;
use App\Models\order\mysql\PayOrderItem; use App\Models\order\mysql\PayOrderItem;
...@@ -82,4 +83,16 @@ class TccController extends Base ...@@ -82,4 +83,16 @@ class TccController extends Base
$params['traceId'] = \Helpers\Tracer::getTraceId(); $params['traceId'] = \Helpers\Tracer::getTraceId();
$this->success($params); $this->success($params);
} }
public function idsAction() {
$number = $this->params['number'] ?? '12';
$count = $this->params['count'] ?? 5;
$res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[
"type" => "55c768",
'number' => $number,
"count" => $count]]);
$id = $res['id_datetime']['55c768'] ?? [];
$this->success(['ids'=>$id]);
}
} }
...@@ -51,12 +51,17 @@ class Hook extends \Yaf\Plugin_Abstract { ...@@ -51,12 +51,17 @@ class Hook extends \Yaf\Plugin_Abstract {
return false; return false;
} }
// 优选选择 raw 方式, raw, $_POST 不能共存。 // raw, $_POST 不能共存。
if(!empty($_POST)) {
return false;
}
// application/json形式,仅处理$_POST, 不整合$_GET // application/json形式,仅处理$_POST, 不整合$_GET
switch ($request->getServer('CONTENT_TYPE')) { switch (strtolower($request->getServer('CONTENT_TYPE'))) {
case "application/json": case "application/json":
case "application/json; charset=utf-8": case "application/json; charset=utf-8":
case "application/json;charset=utf-8":
$jsonPost = file_get_contents("php://input"); $jsonPost = file_get_contents("php://input");
if(empty($jsonPost)) { if(empty($jsonPost)) {
$_POST = []; $_POST = [];
......
<?php <?php
namespace App\Services\order; namespace App\Services;
use App\Exception\custom\CodeSpecialException; use App\Services\channel\Pingxx;
use Helpers\Sdk; use App\Services\channel\Platform;
use App\Exception\custom\PayException;
class OrderService class Channel
{ {
/** /**
* @param $orderId * @param $orderId
* @param $userId * @param $userId
* 调用内部服务获取用户订单、子单、分润信息 * 调用内部服务获取用户订单、子单、分润信息
*/ */
public static function getFullOrderData($order_id) { public static function getChannel($pay_channel) {
$payer = null;
switch ($pay_channel) {
case '105':
case '106':
case '107':
case '108':
case '109':
case '110':
$payer = new Pingxx();
break;
case '1':
$payer = new Platform();
break;
$url = config('interface', 'order.order.pay_order');
if (!$url) {
throw new CodeSpecialException("failed" . __METHOD__ );
} }
$params = ['order_id'=>$order_id]; if(empty($payer)) {
return Sdk::call($url, $params); throw new PayException(['cus'=>100]);
} }
public static function getOrderInfo($order_id, $user_id) { return $payer;
//pay_order_info
$url = config('interface', 'order.order.pay_order_info');
if (!$url) {
throw new CodeSpecialException("failed" . __METHOD__ );
} }
$params = ['order_id'=>$order_id, 'user_id'=>$user_id];
return Sdk::call($url, $params);
}
/**
* @param $order_item_id
* @param $user_id
* @return bool|mixed
* @throws CodeSpecialException
* 获取订单子单信息
*/
public static function getOrderItemData($order_item_id) {
//merchant.account.get_role_list
$url = config('interface', 'order.order.pay_order_item');
if (!$url) {
throw new CodeSpecialException("failed" . __METHOD__ );
}
$params = ['order_item_id'=>$order_item_id];
return Sdk::call($url, $params);
}
} }
\ No newline at end of file
<?php <?php
namespace App\Services\channel;
use Api\PhpUtils\Log\FileLog;
use App\Exception\custom\RefundException;
use App\Models\order\mysql\PlatformConfig;
use App\Services\pingxx\PingxxService;
use App\Exception\custom\PayException;
use App\Exception\BaseException;
class Pingxx {
/**
* @param $pay_order
* @param $mata_data
* 系统支付
*/
public function pay($pay_order, $mata_data, $extra = []) {
// 已经创建,需要查询返回
if(!empty($pay_order['third_order_id'])) {
$ret = PingxxService::getInstance()->getOrder($pay_order['third_order_id']);
} else {
$ret = PingxxService::getInstance()->createOrder($pay_order, $mata_data);
}
if (!empty($ret['error'])) {
throw new BaseException(['msg'=>$ret['error']['message'], 'code'=>'2301']);
}
if ($ret["status"] == 'paid' || $ret['status'] == 'refunded') {
throw new PayException(['cus' => 101]);
} elseif ($ret["status"] == 'canceled') {
throw new PayException(['cus' => 102]);
} elseif ($ret["status"] == 'created') {
;
} else {
throw new PayException(['cus' => 103]);
}
$channel_exist = false;
$channel = $this->getChannel($pay_order['pay_channel']);
if(!empty($ret['charges']['data'])) {
//是否存在
foreach ($ret['charges']['data'] as $ch) {
if($ch['channel'] == $channel) {
$channel_exist = true;
}
}
}
//判断是否做过pay操作 charge
if($channel_exist == false) {
// 创建支付单
$payment = [
'charge_order_no'=> $pay_order['pay_order_id'],
'charge_amount'=> $pay_order['pay_amount'],
'channel'=> $channel,
'extra'=> $extra,
];
$pay = PingxxService::getInstance()->pay($ret['id'], $payment);
if (!empty($pay['error'])) {
throw new BaseException(['msg'=>$pay['error']['message'], 'code'=>'2002']);
}
return $pay;
} else {
return $ret;
}
}
/**
* @param $pay_order
* @param $mata_data
* 系统退款
*/
public function refund($data, $mata_data) {
/*
https://www.pingxx.com/api/%E8%AE%A2%E5%8D%95%20Refunds%20%E9%80%80%E6%AC%BE%E6%A6%82%E8%BF%B0.html
订单退款 paid = true, status = canceled, amount_paid > 0 则表明可以退款
amount_paid 大于 actual_amount 需要最后一单全退。
具体见ping++ 文档
*/
$pay = PingxxService::getInstance()->getOrder($data['pay_order']['third_order_id']);
if(empty($pay['paid']) || $pay['paid'] != true) {
throw new RefundException(['cus' => 7]);
}
// 存在多次支付的场景需要人工介入处理, 修复订单支付金额,退款金额
if($pay['amount_paid'] > $pay['actual_amount']) {
FileLog::error('pay-service: 订单可能存在多次成功支付,需人工确认处理。', json_encode($pay, JSON_UNESCAPED_UNICODE));
throw new RefundException(['cus' => 8]);
}
// 构造退款数据
$refund = [
'id'=>$pay['id'], //pingxx订单ID
'charge'=>$pay['charge'], //pingxx 支付订单ID
'refund_mode' => 'to_source', //默认返回
'charge_amount'=>$data['refund_order']['refund_amount'],
'metadata' => $mata_data,
];
return PingxxService::getInstance()->sendRefund($refund);
}
private function getChannel($pay_method_id) {
$maps = [
1=>"syt", //系统余额支付
105=>"wx", //微信 App 支付
106=>"alipay", //支付宝
107=>"wx_pub", //微信 JSAPI 支付
108=>"wx_pub_qr", //微信 Native 支付
109=>"wx_wap", //微信 H5 支付
110=>"wx_lite", //微信小程序支付
];
if(!empty($maps[$pay_method_id])) {
return $maps[$pay_method_id];
} else {
return $maps[105];
}
}
/**
* @param $pay_order
* @return array
* @throws PayException
* 手续费计算
*/
public function clearChannelTips($pay_order) {
//PlatformConfig
$info = PlatformConfig::get('*', [
'account_type'=>2,
'channel'=>1,
'source_name'=>$pay_order['source_name'],
'service_name'=>$pay_order['service_name']
]);
if(empty($info)) {
throw new PayException(['cus'=>104]);
}
$float_tip = $info['rate'] * $pay_order['payment'] / 10000;
if($float_tip >= 1 ) {
$total_tip = round($float_tip);
return [
'account_id' => $info['account_id'],
'amount' => $total_tip,
];
} else {
return [];
}
}
public function clearPlatformTips($pay_order) {
//PlatformConfig
$info = PlatformConfig::get('*', [
'account_type'=>1,
'channel'=>1,
'source_name'=>$pay_order['source_name'],
'service_name'=>$pay_order['service_name']
]);
if(empty($info)) {
throw new PayException(['cus'=>104]);
}
$float_tip = $info['rate'] * $pay_order['total_price'] / 10000;
$total_tip = round($float_tip);
if($float_tip >= 1) {
return [
'account_id' => $info['account_id'],
'amount' => $total_tip,
];
} else {
return [];
}
}
public function getFundsInfo($pay_order) {
$info = PlatformConfig::get('*', [
'account_type'=>3,
'channel'=>1,
'source_name'=>$pay_order['source_name'],
'service_name'=>$pay_order['service_name']
]);
if(empty($info)) {
throw new PayException(['cus'=>104]);
}
return $info;
}
}
<?php <?php
namespace App\Services\channel;
class Platform {
/**
* @param $pay_order
* @param $mata_data
* 系统支付
*/
public function pay($pay_order, $mata_data) {
//todo 保存数据
return [];
}
/**
* @param $pay_order
* @param $mata_data
* 系统退款
*/
public function refund($pay_order, $mata_data) {
}
}
\ No newline at end of file
<?php <?php
namespace App\Services\wallet; namespace App\Services\marketing;
use App\Exception\custom\CodeSpecialException;
use Helpers\Sdk;
use App\Models\order\mysql\MarketingAccount;
use App\Models\order\mysql\MarketingAccountLog;
use App\Models\order\mysql\MarketingAccountMap;
class WalletService
class AccountService
{ {
private static $maps = [];
/**
* @param $list
* @param $order_id
* @return bool
* 资金冻结
*/
public static function frozen($list, $order_id) {
$coupon = self::formatData($list);
$cnt = true;
foreach ($coupon as $id=>$amount) {
$marketing_account_id = self::map($id);
$cnt = $cnt && MarketingAccount::update([
'frozen_amount[+]'=>$amount,
'receivable_amount[-]'=>$amount,
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$id,
'fund_type'=>1,
'trade_id'=>$order_id,
'amount'=>$amount,
], ['rowCount'=>true]);
}
return $cnt;
}
/** /**
* @param $params * @param $list
* @return bool|mixed * @param $order_id
* @throws CodeSpecialException * @return bool
* 钱包记录同步 * 营销资金扣款
*/ */
public static function send($params) { public static function pay($list, $order_id) {
$coupon = self::formatData($list);
$url = config('interface', 'wallet.account.transfer'); $cnt = true;
if (!$url) { // 冻结优惠信息
throw new CodeSpecialException("failed"); foreach ($coupon as $id=>$amount) {
$marketing_account_id = self::map($id);
$cnt = $cnt && MarketingAccount::update([
'frozen_amount[-]'=>$amount,
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$id,
'fund_type'=>2,
'trade_id'=>$order_id,
'amount'=>$amount,
], ['rowCount'=>true]);
}
return $cnt;
} }
// 服务增加签名 /**
$appid = \Yaf\Application::app()->getConfig()->get('wallet.appid'); * @param $list
$secret = \Yaf\Application::app()->getConfig()->get('wallet.secret'); * @param $refund_order_id
* @return bool
* 退款营销可用增加
*/
public static function refund($list, $refund_order_id) {
$coupon = self::formatData($list);
$cnt = true;
$params['time'] = date('Y-m-d H:i:s'); foreach ($coupon as $id=>$amount) {
$params['transfer_service_id'] = $appid; $marketing_account_id = self::map($id);
$params['sign'] = self::sign($params, $secret); $cnt = $cnt && MarketingAccount::update([
'receivable_amount[+]'=>$amount,
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$id,
'fund_type'=>3,
'trade_id'=>$refund_order_id,
'amount'=>$amount,
], ['rowCount'=>true]);
}
return $cnt;
}
/**
* @param $list
* @param $order_id
* @return bool
* 未支付取消,营销冻结释放
*/
public static function cancel($list, $order_id) {
$coupon = self::formatData($list);
$cnt = true;
return Sdk::call($url, $params); foreach ($coupon as $id=>$amount) {
$marketing_account_id = self::map($id);
$cnt = $cnt && MarketingAccount::update([
'receivable_amount[+]'=>$amount,
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$id,
'fund_type'=>4,
'trade_id'=>$order_id,
'amount'=>$amount,
], ['rowCount'=>true]);
}
return $cnt;
}
/**
* @param $data
* @param $marketing_account_id
* @return bool
* 营销充值
*/
public static function recharge($data, $marketing_account_id) {
$cnt = true;
$cnt = $cnt && MarketingAccount::update([
'receivable_amount[+]'=>$data['amount']
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$data['third_id'],
'fund_type'=>5,
'trade_id'=>$data['trade_id'],
'amount'=>$data['amount'],
]);
return $cnt;
}
public static function transfer($data, $marketing_account_id) {
$cnt = true;
$cnt = $cnt && MarketingAccount::update([
'receivable_amount[-]'=>$data['amount']
], ['marketing_account_id'=>$marketing_account_id]);
$cnt = $cnt && MarketingAccountLog::insert([
'marketing_account_id'=>$marketing_account_id,
'third_id'=>$data['third_id'],
'fund_type'=>6,
'trade_id'=>$data['trade_id'],
'amount'=>$data['amount'],
]);
return $cnt;
}
public static function map($third_id, $source_name = 10, $service_name = 1) {
if(!isset(self::$maps[$third_id])) {
$info = MarketingAccountMap::get('*',
['third_id'=>$third_id, 'source_name'=>$source_name, 'service_name'=>$service_name]);
if($info) {
self::$maps[$third_id] = $info['marketing_account_id'];
} else {
self::$maps[$third_id] = '';
}
}
return self::$maps[$third_id];
}
private static function formatData($list) {
$coupon = [];
foreach ($list as $r) {
if(!isset($coupon[$r['capital_pool_id']])) {
$coupon[$r['capital_pool_id']] = 0;
}
$coupon[$r['capital_pool_id']] += $r['share_coupon_amount'];
} }
private function sign($params, $secret) { return $coupon;
$sign = md5($params['time'] . $secret);
return $sign;
} }
} }
\ No newline at end of file
<?php <?php
namespace App\Services\refund; namespace App\Services\pay;
use Api\PhpServices\Idgen\Idgen; use App\Exception\custom\FundsException;
use Api\PhpUtils\Log\FileLog; use App\Models\order\mysql\PlatformFunds;
use App\Exception\BaseException; use App\Models\order\mysql\PlatformFundsLog;
use App\Models\Dictionary;
use App\Models\order\mysql\PayOrder;
use App\Models\order\mysql\PayOrderClearing;
use App\Models\order\mysql\PayOrderClearingItem;
use App\Models\order\mysql\PayOrderItem;
use App\Models\order\mysql\RefundOrder;
use App\Services\order\OrderService;
use App\Services\pingxx\PingxxService; class FundsService
use Helpers\Strategy;
use App\Exception\custom\RefundException;
use Yaf\Application;
class RefundService
{ {
/**
* @param $order_item_id
* @param $user_id
* @throws RefundException
* 发起退款请求
*/
public function do_refund($order_id, $order_item_ids = []) {
if(empty($order_id)) {
throw new RefundException(['cus' => 1]);
}
$data = $this->can_refund($order_id, $order_item_ids);
/*
https://www.pingxx.com/api/%E8%AE%A2%E5%8D%95%20Refunds%20%E9%80%80%E6%AC%BE%E6%A6%82%E8%BF%B0.html
订单退款 paid = true, status = canceled, amount_paid > 0 则表明可以退款
amount_paid 大于 actual_amount 需要最后一单全退。
具体见ping++ 文档
*/
$pay = PingxxService::getInstance()->getOrder($data['pay_order']['third_order_id']);
if(empty($pay['paid']) || $pay['paid'] != true) {
throw new RefundException(['cus' => 12]);
}
// 存在多次支付的场景需要人工介入处理, 修复订单支付金额,退款金额
if($pay['amount_paid'] > $pay['actual_amount']) {
FileLog::error('pay-service: 订单可能存在多次成功支付,需人工确认处理。', json_encode($pay, JSON_UNESCAPED_UNICODE));
throw new RefundException(['cus' => 13]);
}
// 构造退款数据
$refund = [
'id'=>$pay['id'], //pingxx订单ID
'charge'=>$pay['charge'], //pingxx 支付订单ID
'refund_mode' => 'to_source', //默认返回
'charge_amount'=>$data['refund_order']['refund_amount'],
'metadata' => [
'app_id'=>'merchant-c',
'pingxx_id'=>PingxxService::getInstance()->getAppId(),
'environ'=>Application::app()->environ(),
'refund_order_id'=>$data['refund_order']['refund_order_id'],
'order_item_id'=>$data['order_item_id'],
'order_id'=>$order_id,
],
];
try{
$ret = PingxxService::getInstance()->sendRefund($refund);
if(!empty($ret["data"])) {
$edit = ['request_pingxx_success_time'=>date('Y-m-d H:i:s')];
RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]);
} else {
throw new BaseException(['msg'=>$ret['error']['message'], 'code'=>'2011']);
}
return [
'order_item_id'=>$data['order_item_id'],
'refund_order_id'=>$data['refund_order']['refund_order_id'],
];
}catch (\Exception $e) {
$edit = ['refund_order_status'=>0];
RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]);
PayOrderItem::update(['refund_order_status'=>0], ['order_item_id'=>$data['order_item_id']]);
throw $e;
}
}
/** /**
* @param $data * @param $data
* @return array * @param int $channel
* @throws RefundException * @param int $source_name
* 退款回调 * @param int $service_name
* @throws FundsException
* 渠道账户入账记录
*/ */
public function call_back($data) { public static function credit($data, $channel = 1, $source_name = 10, $service_name = 1) {
if (!in_array($data['type'], ['refund.succeeded', 'order.refunded'])) {
return ['error'=>'退款回调类型不正确'];
}
if (empty($data['data']['object'])) {
return ['error'=>'退款回调格式不正确'];
}
$object = $data['data']['object'];
if(empty($object['metadata']['app_id']) || $object['metadata']['app_id'] != "merchant-c") {
//非支付中心发出的订单,需要忽略
return ['error'=>'非merchant渠道订单,不做处理。'];
}
if($data['type'] == 'refund.succeeded') { $info = PlatformFunds::getMaster('*',
$refund_order_id = $object['order_no']; ['channel'=>$channel, 'source_name'=>$source_name, 'service_name'=>$service_name]);
$refunded = $object['succeed']; //"succeed": true, if(empty($info)) {
} else { throw new FundsException(['cus'=>1]);
$refund_order_id = $object['merchant_order_no'];
$refunded = $object['refunded'];
} }
if($refunded != true) { $cnt = PlatformFunds::update([
return ['error'=>'退款状态不正确,不做处理。']; 'total_amount[+]'=>$data['amount'],
} 'total_tip[+]'=>$data['tip'],
], ['platform_funds_id'=>$info['platform_funds_id']]);
if(empty($object['metadata']['refund_order_id'])) {
return ['error'=>'退款订单缺少matadata,不做处理。'];
}
if(empty($object['metadata']['order_item_id'])) {
return ['error'=>'退款订单缺少order_item_id,不做处理。'];
}
// 修改状态,锁定业务 $l_cnt = PlatformFundsLog::insert([
RefundOrder::beginTransaction(); 'platform_funds_id'=>$info['platform_funds_id'],
'trade_id'=>$data['order_id'],
'third_order_id'=>$data['third_id'],
'fund_type'=>1,
'amount'=>$data['amount'],
'tip'=>$data['tip'],
], ['rowCount' => true]);
$refund_order_id = $object['metadata']['refund_order_id']; return $l_cnt && $cnt;
$edit = [
'refund_order_status'=>2, //pingxx回调成功,
'pingxx_callback_success_time'=>date('Y-m-d H:i:s'), //回调成功时间
];
$cnt_r = RefundOrder::update($edit, ['refund_order_id'=>$refund_order_id, 'refund_order_status'=>1]);
$cnt_i = PayOrderItem::update(['refund_order_status'=>2], [
'order_item_id'=>$object['metadata']['order_item_id']
]);
if($cnt_r && $cnt_i) {
RefundOrder::commit();
} else {
RefundOrder::rollback();
} }
$refund = RefundOrder::getMaster('*', ['refund_order_id'=>$refund_order_id]);
if($refund['refund_order_status'] != 2) {
throw new RefundException(['cus' => 16]);
}
return ['refund_order_id'=>$refund_order_id];
//不用搞清分核算,是否需要平台记账
}
/**
* @param $where
* @return \Api\PhpUtils\Mysql\MysqlBase|array
* @throws RefundException
* 判断订单是否可退,判断是否存在退款申请
*/
private function can_refund($order_id, $order_item_ids) {
/** /**
* 业务方自行解决,订单关闭、订单超过时长不允许退款等逻辑 * @param $data
* 支付中心退款判断仅做主单是否支付,主单是否支持退款的判断 * @param int $channel
* @param int $source_name
* @param int $service_name
* @throws FundsException
* 渠道账户出账
*/ */
$refund_payment = 0; public static function payOut($data, $channel = 1, $source_name = 10, $service_name = 1) {
try{
RefundOrder::beginTransaction();
$pay_order = PayOrder::getMaster('*', ['order_id'=>$order_id]);
if(empty($pay_order)) {
throw new RefundException(['cus' => 15]);
}
$pay_order_items = PayOrderItem::selectMaster('*', ['order_id'=>$order_id]);
if(empty($pay_order_items)) {
throw new RefundException(['cus' => 4]);
}
foreach ($pay_order_items as $item) {
if($item['can_notify_account'] == 1) {
continue; //已结算不允许退款
}
if(in_array($item['refund_order_status'], [1, 2])) {
continue; //退款中、已退款不允许再次申请
}
if(!empty($order_item_ids) && !in_array($item['order_item_id'], $order_item_ids)) { $info = PlatformFunds::getMaster('*',
continue; //部分退款,且不再退款子单列表,不处理 ['channel'=>$channel, 'source_name'=>$source_name, 'service_name'=>$service_name]);
if(empty($info)) {
throw new FundsException(['cus'=>1]);
} }
$items[] = $item; $cnt = true;
$cnt = $cnt && PlatformFunds::update([
'total_amount[-]'=>$data['amount'],
'total_tip[-]'=>$data['tip'],
], ['platform_funds_id'=>$info['platform_funds_id']]);
$refund_payment += $item['payment']; $cnt = $cnt && PlatformFundsLog::insert([
} 'platform_funds_id'=>$info['platform_funds_id'],
'trade_id'=>$data['order_id'],
'third_order_id'=>$data['third_id'],
'fund_type'=>2,
'amount'=>$data['amount'],
'tip'=>$data['tip'],
], ['rowCount' => true]);
if(empty($items)) { return $cnt;
throw new RefundException(['cus' => 14]); //
} }
//重新计算微信手续费
$cleared_amount = $cleared_tip = 0;
// 计算已退金额、已退手续费 sum(refund_wx_tip), sum(refund_amount)
$list = RefundOrder::selectMaster('*', ['order_id'=>$pay_order['order_id']]);
foreach ($list as $r) {
if($r['refund_order_status'] == Dictionary::REFUND_ORDER_STATUS_UNDO) {
continue;
}
$cleared_amount += $r['refund_amount'];
$cleared_tip += $r['refund_wx_tip'];
}
// 总共手续费
$wx_clear = PayOrderClearing::getMaster('*', [
'order_id'=>$pay_order['order_id'],
'pay_order_id'=>$pay_order['pay_order_id'],
'pay_sub_type'=>406 //三方渠道手续费
]
);
$wx_total_tip = 0;
if($wx_clear) {
$wx_total_tip = $wx_clear['pay_amount'];
}
$wx_tip = Strategy::getWxTip($refund_payment, $pay_order['pay_amount'], $cleared_amount, $wx_total_tip, $cleared_tip);
$refund = [
'refund_order_id'=>$this->gen_refund_order_id($pay_order['user_id']),
'user_id'=>$pay_order['user_id'],
'order_id'=>$pay_order['order_id'],
'refund_order_status'=>1,
'refund_amount'=>$refund_payment,
'refund_wx_tip'=>$wx_tip,
'source_name'=>$pay_order['source_name'],
'extra'=> json_encode(['order_item_id'=>array_column($items, 'order_item_id')]),
];
$cnt = RefundOrder::insert($refund, ['rowCount'=>true]);
if($cnt == 0) {
throw new RefundException(['cus' => 0]);
}
//批量更新子单状态 "user_id" => [2, 123, 234, 54],
PayOrderItem::update(['refund_order_status'=>1], ['order_item_id'=>array_column($items, 'order_item_id')]);
$data = [
'refund_order'=>$refund,
'pay_order'=>$pay_order,
'order_item_id'=>array_column($items, 'order_item_id'),
];
RefundOrder::commit();
}catch (\PDOException $e) {
RefundOrder::rollback();
throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]);
}
return $data;
}
private function gen_refund_order_id($user_id)
{
$number = substr($user_id, -2);
$ids = $this->get_idgen_id($number, 1);
return array_pop($ids);
}
private function get_idgen_id($number, $count = 1)
{
$number = intval($number);
$res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[
"type" => "55c768",
'number' => $number,
"count" => $count]]);
$id = $res['id_datetime']['55c768'] ?? [];
return $id;
}
} }
\ No newline at end of file
...@@ -5,14 +5,22 @@ namespace App\Services\pay; ...@@ -5,14 +5,22 @@ namespace App\Services\pay;
use Api\PhpServices\Idgen\Idgen; use Api\PhpServices\Idgen\Idgen;
use Api\PhpUtils\Log\FileLog; use Api\PhpUtils\Log\FileLog;
use App\Models\Dictionary;
use App\Models\order\mysql\PayOrder; use App\Models\order\mysql\PayOrder;
use App\Models\order\mysql\PayOrderItem; use App\Models\order\mysql\PayOrderItem;
use App\Models\order\mysql\PayCouponItem;
use App\Models\order\mysql\PayOrderClearing; use App\Models\order\mysql\PayOrderClearing;
use App\Models\order\mysql\PayOrderClearingItem; use App\Models\order\mysql\PayOrderClearingItem;
use App\Models\order\mysql\WriteOffMerchantRecord;
use App\Models\order\mysql\WriteOffOrder;
use App\Models\order\mysql\WriteOffOrderItem;
use App\Models\order\mysql\WriteOffPersonalRecord;
use App\Models\order\mysql\WriteOffPlatformRecord;
use App\Services\Channel;
use App\Services\marketing\AccountService;
use App\Services\order\OrderService; use App\Services\order\OrderService;
use App\Services\pingxx\PingxxService; use App\Services\pingxx\PingxxService;
use App\Services\wallet\MerchantAccountService;
use App\Services\wallet\WalletService; use App\Services\wallet\WalletService;
use Helpers\Strategy; use Helpers\Strategy;
use Helpers\Sdk; use Helpers\Sdk;
...@@ -32,191 +40,224 @@ class PayService ...@@ -32,191 +40,224 @@ class PayService
private $pay_order = []; private $pay_order = [];
private $clear_list = []; private $clear_list = [];
private $clear_items_list = []; private $clear_items_list = [];
private $gen_ids = [];
public function do_pay($order_id, $user_id, $pay_method_id = 105, $extra = [])
{ /**
if(empty($order_id)) { * @param $params
* @return mixed
* @throws PayException
* @throws \App\Exception\custom\CodeSpecialException
* 支付订单校验
*/
private function pay_validate($params) {
if($params['order_id']) {
throw new PayException(['cus' => 1]); throw new PayException(['cus' => 1]);
} }
// 获取订单信息
$data = OrderService::getFullOrderData($order_id); if($params['user_id']) {
if (empty($data['result']['order_info'])) {
throw new PayException(['cus' => 2]); throw new PayException(['cus' => 2]);
} }
$order_info = $data['result']['order_info']; // 获取订单 + 子单信息
if ($order_info['user_id'] != $user_id) { $data = OrderService::getFullOrderData($params['order_id']);
if (empty($data['result']['order_info']) || empty($data['result']['order_item_list'])) {
throw new PayException(['cus' => 3]); throw new PayException(['cus' => 3]);
} }
if (empty($order_info['pay_expiration_time'])) { $order_info = $data['result']['order_info'];
throw new PayException(['cus' => 17]); if ($order_info['user_id'] != $params['user_id']) {
throw new PayException(['cus' => 4]);
} }
$pay_expiration_time = strtotime($order_info['pay_expiration_time']); if (empty($order_info['pay_expiration_time'])) {
if($pay_expiration_time < time()) { throw new PayException(['cus' => 5]);
throw new PayException(['cus' => 16]);
} }
if(empty($data['result']['order_item_list'])) { if(strtotime($order_info['pay_expiration_time']) < time()) {
throw new PayException(['cus' => 14]); throw new PayException(['cus' => 6]);
} }
$metadata = [ return $data['result'];
'app_id'=>'merchant-c', }
'pingxx_id'=>PingxxService::getInstance()->getAppId(),
'environ'=>Application::app()->environ(),
'order_id'=>$order_id,
'goods'=>[],
'marketing'=>[],
'order_item_id'=>[],
];
$min_expire_time = $now = time(); /**
foreach ($data['result']['order_item_list'] as $item) { * @param $data
* @param $pay_method_id
* @return array
* @throws PayException
* 创建/获取支付订单信息
*/
private function make_pay_order($data, $pay_method_id) {
if(strtotime($item['expiration_time']) < $min_expire_time) { $order = $data["order_info"];
$min_expire_time = strtotime($item['expiration_time']); $pay_expiration_time = strtotime($order['pay_expiration_time']);
} $now = time();
if(empty($metadata['goods'][$item['goods_sku_id']])) { if($pay_expiration_time - $now < 70 ) {
$metadata['goods'][$item['goods_sku_id']] = 0; $pay_expiration_time = $now + 70;
} }
$metadata['goods'][$item['goods_sku_id']] += $item['goods_num']; $ids = $this->get_idgen_id($order["user_id"], 1 + count($data['order_item_list']));
if(!empty($item['marketing_id'])) { $pay_order_id = array_shift($ids);
$metadata['marketing'][$item['goods_sku_id']] = $item['marketing_id'];
}
array_push($metadata['order_item_id'], $item['order_item_id']);
}
if($now > $min_expire_time) { PayOrder::beginTransaction();
throw new PayException(['cus' => 15]); $pay = self::getMaster('*', ['order_id'=>$order['order_id']]);
if(!empty($pay)) {
//更新支付渠道
if($pay['pay_order_status'] == 0) {
PayOrder::update(['pay_channel'=>$pay_method_id],
['pay_order_id'=>$pay['pay_order_id']]
);
} }
PayOrder::commit();
//订单过期时间必须满足 70s+ return $pay;
if($pay_expiration_time - $now < 70 ) {
$pay_expiration_time = $now + 70;
} }
$pay_order_id = $this->gen_pay_order_id($user_id); // 新增支付订单
$new_pay_order = [ $new_pay = [
'pay_order_id' => $pay_order_id, 'pay_order_id' => $pay_order_id,
'user_id' => $order_info['user_id'], 'user_id' => $order['user_id'],
'life_account_id' => $order_info['life_account_id'], 'life_account_id' => $order['life_account_id'],
'shop_id' => $order_info['shop_id'], 'shop_id' => $order['shop_id'],
'order_id' => $order_info['order_id'], 'order_id' => $order['order_id'],
'pay_order_status' => Dictionary::O_PAY_STATUS_UNPAY, 'pay_order_status' => 0,
'pay_amount' => $order_info['payment'], 'total_price' => $order['total_price'],
'discount_fee' => $order['discount_fee'],
'pay_amount' => $order['payment'],
'pay_channel' => $pay_method_id, 'pay_channel' => $pay_method_id,
'third_order_id' => '', 'third_order_id' => '',
'expire_time' => date('Y-m-d H:i:s', $pay_expiration_time), 'expire_time' => date('Y-m-d H:i:s', $pay_expiration_time),
'source_name' => 10, 'source_name' => 10,
'service_name' => 1, 'service_name' => 1,
'business_from' => $order_info['business_from'], 'business_from' => $order['business_from'],
'extra' => json_encode([]), 'extra' => json_encode([]),
]; ];
//获取或创建支付订单 $cnt = true;
$pay_order = PayOrder::get_valid_order($order_id, $new_pay_order); $cnt = $cnt && PayOrder::insert($new_pay, ['rowCount'=>true]);
if (empty($pay_order)) {
throw new PayException(['cus' => 4]); $order_items = $coupon_items = [];
foreach ($data['order_item_list'] as $r) {
$order_items[] = [
'pay_order_item_id'=>array_shift($ids),
'pay_order_id'=>$pay_order_id,
'order_id'=>$r['order_id'],
'order_item_id'=>$r['order_item_id'],
'user_id'=>$r['user_id'],
'payment'=>$r['payment'],
'share_coupon'=>$r['share_coupon'],
];
} }
//不支持修改订单过期时间,再次调用支付服务 //新增订单子单
if(strtotime($pay_order['expire_time']) < time()) { $cnt = $cnt && PayOrderItem::insert($order_items, ['rowCount'=>true]);
throw new PayException(['cus' => 16]);
if(!empty($data['order_share_coupon'])) {
foreach ($data['order_share_coupon'] as $r) {
$coupon_items[] = [
'pay_order_id'=>$pay_order_id,
'order_id'=>$r['order_id'],
'user_id'=>$r['user_id'],
'order_item_id'=>$r['order_item_id'],
'coupon_id'=>$r['coupon_id'],
'capital_pool_id'=>$r['capital_pool_id'],
'coupon_amount'=>$r['coupon_amount'],
'share_coupon_amount'=>$r['share_coupon_amount'],
];
} }
if ($pay_order['pay_order_status'] == Dictionary::O_PAY_STATUS_PAYED) { //新增订单优惠信息
throw new PayException(['cus' => 5]); $cnt = $cnt && PayCouponItem::insert($coupon_items, ['rowCount'=>true]);
//营销资金冻结
$cnt = $cnt && AccountService::frozen($data['order_share_coupon'], $order['order_id']);
} }
// 已经创建,需要查询返回 if($cnt == true) {
if(!empty($pay_order['third_order_id'])) { $pay = $new_pay;
$ret = PingxxService::getInstance()->getOrder($pay_order['third_order_id']); PayOrder::commit();
} else { } else {
$ret = PingxxService::getInstance()->createOrder($pay_order, $metadata); PayOrder::rollback();
throw new PayException(['cus' => 7]);
} }
if (!empty($ret['error'])) { return $pay;
throw new BaseException(['msg'=>$ret['error']['message'], 'code'=>'2301']);
} }
if ($ret["status"] == 'paid' || $ret['status'] == 'refunded') { /**
$edit = [ * @param $params
'pay_order_status' => Dictionary::O_PAY_STATUS_PAYED, * @param array $extra
'third_order_id' => $ret['id'], * @return array|mixed
]; * @throws PayException
//todo 需要做补偿 * 创建支付订单
//PayOrder::update($edit, ['pay_order_id' => $pay_order['pay_order_id']]); */
throw new PayException(['cus' => 5]); public function do_pay($params, $extra = [])
{
// 支付数据检查
$data = $this->pay_validate($params);
} elseif ($ret["status"] == 'canceled') { // 创建支付订单、营销冻结
$edit = [ $pay_order = $this->make_pay_order($data, $params['pay_method_id']);
'expire_time' => date('Y-m-d H:i:s'), if ($pay_order['pay_order_status'] == 2) {
]; throw new PayException(['cus' => 8]);
//todo 需要做状态同步,不仅仅是更新状态 }
PayOrder::update($edit, ['pay_order_id' => $pay_order['pay_order_id']]);
throw new PayException(['cus' => 6]);
} elseif ($ret["status"] == 'created') { // 支付金额0,系统支付
$edit = [ if($pay_order['pay_amount'] == 0) {
'pay_order_status' => Dictionary::O_PAY_STATUS_WAIT, $params['pay_method_id'] = 1;
'third_order_id' => $ret['id'], }
// 构造支付回调的 mataData
$meta_data = [
'app_id'=>'merchant-c',
'environ'=>Application::app()->environ(),
'order_id'=>$params["order_id"],
'goods'=>[],
'marketing'=>[],
'order_item_id'=>[],
]; ];
PayOrder::update($edit, ['pay_order_id' => $pay_order['pay_order_id']]);
} else { foreach ($data['order_item_list'] as $item) {
throw new PayException(['cus' => 0]); if(empty($meta_data['goods'][$item['goods_sku_id']])) {
$meta_data['goods'][$item['goods_sku_id']] = 0;
} }
$meta_data['goods'][$item['goods_sku_id']] += $item['goods_num'];
//存在一个继续支付单订单 if(!empty($item['marketing_id'])) {
if($pay_order_id != $pay_order['pay_order_id']) { $meta_data['marketing'][$item['goods_sku_id']] = $item['marketing_id'];
$ret['exist'] = true;
} }
$channel_exist = false; array_push($meta_data['order_item_id'], $item['order_item_id']);
$channel = $this->getChannel($pay_method_id);
if(!empty($ret['charges']['data'])) {
//是否存在
foreach ($ret['charges']['data'] as $ch) {
if($ch['channel'] == $channel) {
$channel_exist = true;
}
} }
} try{
$payer = Channel::getChannel($params['pay_method_id']);
$ret = $payer->pay($pay_order, $meta_data, $extra);
//判断是否做过pay操作 charge PayOrder::update(['pay_order_status'=>1],
if($channel_exist == false) { ['pay_order_id'=>$pay_order['pay_order_id'], 'pay_order_status'=>0]);
// 创建支付单
$payment = [
'charge_order_no'=> $pay_order['pay_order_id'],
'charge_amount'=> $pay_order['pay_amount'],
'channel'=> $channel,
'extra'=> $extra,
];
$pay = PingxxService::getInstance()->pay($ret['id'], $payment); }catch (BaseException $e) {
if (!empty($pay['error'])) { throw $e;
throw new BaseException(['msg'=>$pay['error']['message'], 'code'=>'2002']);
}
return $pay; } catch (\Exception $e) {
throw new BaseException(['msg'=>$e->getMessage(), 'code'=>'2301']);
}
} else {
return $ret; return $ret;
} }
}
/** /**
* @param $data
* @return array|string[]
* @throws PayException * @throws PayException
* 回调订单支付 * 回调订单支付
*/ */
public function call_back($data) public function call_back($data)
{ {
if (!in_array($data['type'], ['order.succeeded', 'charge.succeeded'])) { if (!in_array($data['type'], ['charge.succeeded'])) {
return ['error'=>'支付回调类型不正确']; return ['error'=>'支付回调类型不正确'];
} }
...@@ -225,60 +266,46 @@ class PayService ...@@ -225,60 +266,46 @@ class PayService
} }
$object = $data['data']['object']; $object = $data['data']['object'];
if(empty($object['metadata']['app_id']) || $object['metadata']['app_id'] != "merchant-c") { if(empty($object['metadata']['app_id'])
|| substr($object['metadata']['app_id'], 0, 9) != "merchant-") {
//非支付中心发出的订单,需要忽略 //非支付中心发出的订单,需要忽略
return ['error'=>'非merchant渠道订单,不做处理。']; return ['error'=>'非merchant渠道订单,不做处理。'];
} }
if($data['type'] == 'charge.succeeded') { if($object['paid'] != true) {
$pay_order_id = $object['order_no'];
$paid = $object['paid'];
$amount = $object['amount'];
} else {
$pay_order_id = $object['merchant_order_no'];
$paid = $object['paid'];
$amount = $object['actual_amount'];
}
if($paid != true) {
return ['error'=>'支付状态不正确,不做处理。']; return ['error'=>'支付状态不正确,不做处理。'];
} }
$pay_order_id = $object['order_no'];
$pay_order = PayOrder::get('*', ['pay_order_id' => $pay_order_id]); $pay_order = PayOrder::get('*', ['pay_order_id' => $pay_order_id]);
if (empty($pay_order)) { if (empty($pay_order)) {
throw new PayException(['cus' => 7]); throw new PayException(['cus' => 7]);
} }
if($pay_order['pay_amount'] != $amount) { if($pay_order['pay_amount'] != $object['amount']) {
throw new PayException(['cus' => 8]); throw new PayException(['cus' => 8]);
} }
// 修改状态,锁定业务 // 修改状态,锁定业务
$edit = [ $cnt = PayOrder::update([
'pay_order_status' => 2, //已经支付 'pay_order_status' => 2, //已经支付
'pingxx_callback_success_time' => date('Y-m-d H:i:s'), 'pingxx_callback_success_time' => date('Y-m-d H:i:s'),
]; ], [
$where = [
'pay_order_id' => $pay_order_id, 'pay_order_id' => $pay_order_id,
'pay_order_status' => 1, //待支付确认 'pay_order_status' => 1, //待支付确认
]; ]);
$cnt = PayOrder::update($edit, $where);
//保持幂等返回成功 //保持幂等返回成功
if($cnt == 0) { if($cnt == 0) {
return ['pay_order_id' => $pay_order_id]; return ['pay_order_id' => $pay_order_id];
} }
//获取订单 + 子单 + 分销信息
$ret = OrderService::getFullOrderData($pay_order['order_id'], $pay_order['user_id']);
if (empty($ret['result']['order_info'])) {
throw new PayException(['cus' => 1]);
}
$this->pay_order = $pay_order; $this->pay_order = $pay_order;
$payer = Channel::getChannel($pay_order['pay_channel']);
// 生成支付清分单 // 生成支付清分单
$this->make_order_clearing($ret['result']); $this->make_order_clearing($payer);
try{
//收集第一商品名称 //收集第一商品名称
if($this->order_items) { if($this->order_items) {
$title = $this->order_items[0]['goods_name'] ?? '商品名称'; $title = $this->order_items[0]['goods_name'] ?? '商品名称';
...@@ -286,8 +313,6 @@ class PayService ...@@ -286,8 +313,6 @@ class PayService
$title = ''; $title = '';
} }
try{
//通知消息 //通知消息
$app_id = 'wx65e9ba99c333444f'; $app_id = 'wx65e9ba99c333444f';
$template_id = 'EzOH2ZpfW-eUdFf9gzjZTAv-Z9PFmKHNsQ0VOGvvN7c'; $template_id = 'EzOH2ZpfW-eUdFf9gzjZTAv-Z9PFmKHNsQ0VOGvvN7c';
...@@ -300,8 +325,7 @@ class PayService ...@@ -300,8 +325,7 @@ class PayService
date('Y-m-d'), date('Y-m-d'),
]; ];
if(isset($ret['result']['order_info']['business_from']) && $ret['result']['order_info']['business_from'] != 2) { if($pay_order['business_from'] != 2) { //接龙业务不发送消息
//接龙业务不发送消息
Msg::send($pay_order['user_id'], $app_id, $template_id, $page_url, $type, $query); Msg::send($pay_order['user_id'], $app_id, $template_id, $page_url, $type, $query);
} }
...@@ -371,167 +395,344 @@ class PayService ...@@ -371,167 +395,344 @@ class PayService
} }
/** /**
* @param $order_item_id * @param $order_id
* @param $life_account_id * @param $life_account_id
* @param array $order_item_ids
* @return array
* @throws BaseException
* @throws \App\Exception\custom\CodeSpecialException * @throws \App\Exception\custom\CodeSpecialException
* 子订单核销
*/ */
public function write_off($order_item_id, $life_account_id) public function write_off($order_id, $life_account_id, $order_item_ids = [] )
{ {
$cnt = 0;
//锁定状态
try{ try{
PayOrderItem::beginTransaction(); PayOrderItem::beginTransaction();
$pay_order = PayOrder::getMaster('*', ['order_id'=>$order_id]);
if(empty($pay_order) || $pay_order['life_account_id'] != $life_account_id) {
throw new PayException(['cus'=>14]);
}
$item = PayOrderItem::getMaster('*', ['order_item_id' => $order_item_id]); $list = PayOrderItem::getMaster('*', ['pay_order_id' => $pay_order['pay_order_id']]);
if(empty($item)) { if(empty($list)) {
throw new PayException(['cus'=>10]); throw new PayException(['cus'=>15]);
} }
if($item['refund_order_status'] != 0) { $item_ids = [];
throw new PayException(['cus'=>9]); foreach ($list as $item) {
if(in_array($item['refund_order_status'], [1, 2])) {
continue; //退款中、已退款不允许再次申请
}
if($item['can_notify_account'] == 1) {
continue; //已结算不允许再次核销
}
if(!empty($order_item_ids) && !in_array($item['order_item_id'], $order_item_ids)) {
continue; //部分核销,且不再核销子单列表,不处理
} }
$pay_order = PayOrder::getMaster('*', ['pay_order_id'=>$item['pay_order_id']]); $item_ids[] = $item['order_item_id'];
if(empty($pay_order) || $pay_order['life_account_id'] != $life_account_id) { }
throw new PayException(['cus'=>11]);
if(empty($item_ids)) {
throw new PayException(['cus'=>16]);
}
$ids = $this->gen_pay_order_id($life_account_id, count($list) + 5);
$write_off_order_id = array_shift($ids);
//统计平台、团长、商户金额
$cnt = true;
$cnt = $cnt && WriteOffOrder::insert([
'write_off_order_id'=>$write_off_order_id,
'user_id'=>$pay_order['user_id'],
'order_id'=>$pay_order['order_id'],
'write_off_status'=>0,
], ['rowCount' => true]);
$items = [];
foreach ($item_ids as $_id) {
$items[] = [
'writeoff_order_item_id'=>array_shift($ids),
'writeoff_order_id'=>$write_off_order_id,
'order_item_id'=>$_id,
];
} }
$cnt = PayOrderItem::update([ $cnt = $cnt && WriteOffOrderItem::insert($items, ['rowCount' => true]);
$cnt = $cnt && $this->write_off_clear_record($write_off_order_id, $pay_order, $item_ids);
$cnt = $cnt && PayOrderItem::update([
'notify_account_status' => 0, 'notify_account_status' => 0,
'can_notify_account' => 1, 'can_notify_account' => 1,
'can_notify_account_time' => date('Y-m-d H:i:s'), 'can_notify_account_time' => date('Y-m-d H:i:s'),
], ['order_item_id' => $order_item_id, 'can_notify_account'=>0, 'refund_order_status'=>0]); ], ['order_item_id' => $item_ids, 'can_notify_account'=>0, 'refund_order_status'=>0]);
if($cnt == false) {
throw new PayException(['cus'=>17]);
}
PayOrderItem::commit(); PayOrderItem::commit();
}catch (PayException $e) {
PayOrderItem::rollback();
throw $e;
}catch (\Exception $e) { }catch (\Exception $e) {
PayOrderItem::rollback(); PayOrderItem::rollback();
throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]); throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]);
} }
if($cnt == 0) {//幂等直接返回 $this->sync_account_wallet($write_off_order_id);
return ['order_item_id' => $order_item_id];
}
$this->sync_account_wallet($order_item_id);
return ['order_item_id' => $order_item_id]; return [
'write_off_order_id'=>$write_off_order_id,
'order_id'=>$order_id,
'order_item_id' => $item_ids
];
} }
/** /**
* @param $order_item_id * @param $write_off_order_id
* @throws \App\Exception\custom\CodeSpecialException * @return bool
* 同步钱包处理
*/ */
public function sync_account_wallet($order_item_id) { public function sync_account_wallet($writeOff_order_id) {
$cnt = PayOrderItem::update([ $p_cnt = $u_cnt = $m_cnt = true;
'notify_account_status' => 1,
], ['order_item_id' => $order_item_id, 'can_notify_account' => 1, 'notify_account_status'=>0]);
if($cnt ==0) {//乐观锁,不满足条件不处理 //修改状态为执行中
$cnt = WriteOffOrder::update([
'notify_account_status'=>1,
], ['write_off_order_id'=>$writeOff_order_id, 'write_off_status' => 2, 'notify_account_status'=>0]);
if(!$cnt) {//乐观锁,不满足条件不处理
return true; return true;
} }
//注意查找需要保证pdo是一个,避免数据操作超时 //平台收入入账
$clearing_items = PayOrderClearingItem::selectMaster('*', $p = WriteOffPlatformRecord::selectMaster('*',
['order_item_id' => $order_item_id] ['writeoff_order_id'=>$writeOff_order_id, 'notify_account_status[<]'=>2]);
); if($p) {
$p_cnt = $p_cnt && PlatformAccountService::credit(['trade_id'=>$writeOff_order_id, 'amount'=>$p['payment']]);
$p_cnt = $p_cnt && WriteOffPlatformRecord::update([
'notify_account_status'=>2,
'notify_account_success_time'=>date('Y-m-d H:i:s'),
], ['writeoff_order_id'=>$writeOff_order_id]);
}
//团长分销入账
$us = WriteOffPersonalRecord::getMaster('*',
['writeoff_order_id'=>$writeOff_order_id, 'notify_account_status[<]'=>2]);
if($us) {
$w_list = [];
foreach ($us as $u) {
$w_list[] = [
'user_id' => $u['account_id'],
'service_name' => 10,
'source_name' => 1,
'amount' => $u['payment'],
'third_order_id' => $u['writeoff_personal_id'],
'third_order_id_type' => 406,
'third_order_desc' => '',
];
}
$wallet_list = []; try{
foreach ($clearing_items as $row) { WalletService::send(['list'=>$w_list]);
if($row['pay_amount'] <= 0) { $u_cnt = $u_cnt && WriteOffPlatformRecord::update([
continue; 'notify_account_status'=>2,
'notify_account_success_time'=>date('Y-m-d H:i:s'),
], ['writeoff_order_id'=>$writeOff_order_id]);
}catch (\Exception $e) {
$u_cnt = false;
}
}
//商家收益 407
$m = WriteOffMerchantRecord::selectMaster('*',
['writeoff_order_id'=>$writeOff_order_id, 'notify_account_status[<]'=>2]);
if($m) {
try{ //MerchantAccountService
MerchantAccountService::sync([
'life_account_id'=>$m['life_account_id'],
'shop_id'=>$m['shop_id'],
'source_name'=>$m['source_name'],
'service_name'=>$m['service_name'],
'write_off_order_id'=>$m['write_off_order_id'],
'third_order_title'=>$m['title'],
'third_order_desc'=>$m['desc'],
'third_order_type'=>$m['business_from'],
'amount'=>$m['payment'],
]);
$m_cnt = $m_cnt && WriteOffPlatformRecord::update([
'notify_account_status'=>2,
'notify_account_success_time'=>date('Y-m-d H:i:s'),
], ['writeoff_order_id'=>$writeOff_order_id]);
}catch (\Exception $e) {
$m_cnt = false;
}
} }
//处理商家结算价格的兼容 if($m_cnt && $u_cnt && $p_cnt) {
if($row['clear_payment'] > 0 && $row['pay_sub_type'] == 102) { WriteOffOrder::update([
$pay_amount = $row['clear_payment']; 'notify_account_status'=>2,
'notify_account_success_time'=>date('Y-m-d H:i:s'),
], ['writeoff_order_id'=>$writeOff_order_id, 'notify_account_status'=>1]);
} else { } else {
$pay_amount = $row['pay_amount']; $info = WriteOffOrder::selectMaster('*', ['writeoff_order_id'=>$writeOff_order_id]);
$next = $this->getNextTime($info['can_notify_account_time']);
WriteOffOrder::update([
'notify_account_status'=>0,
'can_notify_account_time[+]'=>1,
'notify_account_times'=>date('Y-m-d H:i:s', $next),
], ['writeoff_order_id'=>$writeOff_order_id]);
}
} }
$wallet_list[] = [ /**
'user_id' => $row['account_id'], * @param $writeOff_order_id
'service_name' => 10, * @param array $order_item_ids
'source_name' => 1, * @return bool
'amount' => $pay_amount, */
'third_order_id' => $row['pay_order_clearing_item_id'], private function write_off_clear_record($writeOff_order_id, $pay_order, $order_item_ids = []) {
'third_order_id_type' => $row['pay_sub_type'],
'third_order_desc' => $row['remark'], $clear_items = PayOrderClearingItem::selectMaster('*',
['order_item_id'=>$order_item_ids]);
$d = $m = $p = [];
foreach ($clear_items as $r) {
if($r['pay_sub_type'] == 404) { // 团长佣金收入
if(!isset($d[$r['account_id']])) {
$d[$r['account_id']] = [
'writeoff_merchant_id'=>array_shift($this->gen_ids),
'writeOff_order_id'=>$writeOff_order_id,
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'user_id'=>$r['account_id'],
'payment'=>0,
]; ];
} }
$d[$r['account_id']]['payment'] += $r['pay_amount'];
} elseif ($r['pay_sub_type'] == 405 || $r['pay_sub_type'] == 408) { // 平台抽成
if(!isset($p[$r['account_id']])) {
$p[$r['account_id']] = [
'refund_platform_id'=>array_shift($this->gen_ids),
'refund_order_id'=>$writeOff_order_id,
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'account_id'=>$r['account_id'],
'payment'=>0,
];
}
$p[$r['account_id']]['payment'] += $r['pay_amount'];
} elseif ($r['pay_sub_type'] == 407) { // 商品交易
$key = $r['life_account_id'] . '_' . $r['shop_id'];
if(!isset($m[$key])) {
$m[$key] = [
'writeoff_merchant_id'=>array_shift($this->gen_ids),
'writeoff_order_id'=>$writeOff_order_id,
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'life_account_id'=>$pay_order['life_account_id'],
'shop_id'=>$pay_order['shop_id'],
'source_name'=>$pay_order['source_name'],
'service_name'=>$pay_order['service_name'],
'business_from'=>$pay_order['business_from'],
'title'=>'',
'desc'=>'',
'payment'=>0,
];
}
$pre = ',';
if(empty($m[$key]['title'])) {
$m[$key]['title'] = $r['remark'];
$pre = '';
}
$m[$key]['desc'] .= $pre . $r['remark'];
$m[$key]['payment'] += $r['pay_amount'];
try{ } else {}
if($wallet_list) { //同步成功,返回结果
WalletService::send(['list'=>$wallet_list]);
} }
PayOrderItem::update([
'notify_account_status' => 2,
'notify_account_times[+]' => 1,
'notify_account_success_time' => date('Y-m-d H:i:s')
], ['order_item_id' => $order_item_id, 'can_notify_account' => 1]);
} catch (\Exception $e) { $cnt = true;
//补偿处理 $cnt = $cnt && WriteOffMerchantRecord::insert(array_values($m), ['rowCount' => true]);
$item = PayOrderItem::getMaster('*', ['order_item_id' => $order_item_id]);
$time = $this->getNextTime($item['notify_account_times']); //计算下次同步的时间
PayOrderItem::update([
'notify_account_status' => 0,
'notify_account_times[+]' => 1,
'can_notify_account_time' => date('Y-m-d H:i:s', $time),
], ['order_item_id' => $order_item_id, 'can_notify_account' => 1]);
FileLog::error('pay-service:wallet: 钱包同步失败', json_encode($wallet_list, JSON_UNESCAPED_UNICODE), $e); if($p) {
$cnt = $cnt && WriteOffPlatformRecord::insert(array_values($p), ['rowCount' => true]);
} }
if($d) {
$cnt = $cnt && WriteOffPersonalRecord::insert(array_values($d), ['rowCount' => true]);
}
return $cnt;
} }
/** /**
* @param $data * @param $payer
* 构造订单流水 * @throws PayException
* 负责支付的清分
*/ */
private function make_order_clearing($data) private function make_order_clearing($payer)
{ {
$this->order_info = $data['order_info']; /**
$this->order_items = $data['order_item_list']; * 1. 总账入账,补贴扣减
* 2. 手续费、团长、平台、商户
* 3. 用户支付 + 营销补贴 = 手续费 + 团长佣金 + 平台抽成 + 商户收入
*/
$this->clear_list = []; $this->clear_list = [];
$this->clear_items_list = []; $this->clear_items_list = [];
$this->order_items = PayOrderItem::selectMaster('*',
['pay_order_id'=>$this->pay_order['pay_order_id']]);
$pay_order_items = []; $coupon_items = PayCouponItem::selectMaster('*', ['pay_order_id'=>$this->pay_order['pay_order_id']]);
foreach ($this->order_items as $r) {
$pay_order_items[] = [
'pay_order_item_id' => $this->gen_pay_order_item_id(),
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'order_item_id' => $r['order_item_id'],
'payment' => $r['payment'],
'user_id' => $this->pay_order['user_id'],
];
}
$wx_tip = $this->make_tip_clearing(); //微信手续费 //生成IDS
$this->get_idgen_id($this->pay_order['user_id'], 3 + count($this->order_items) * 6);
$wx_tip = $this->make_tip_clearing($payer); //渠道手续费
$distribution_tip = $this->make_distribution_clearing(); //团长收益 $distribution_tip = $this->make_distribution_clearing(); //团长收益
$platform_tip = $this->make_platform_clearing(); //平台收益 $platform = $this->make_platform_clearing(); //平台收益
$merchant_cash = $this->order_info['payment'] - $wx_tip - $distribution_tip - $platform_tip;
$merchant_cash = $this->pay_order['pay_amount'] + $this->pay_order['pay_discount'] - $wx_tip - $distribution_tip - $platform['amount'];
//核对各项资金的合理性 //核对各项资金的合理性
if($wx_tip <0 || $distribution_tip <0 || $platform_tip < 0 || $merchant_cash < 0) { if($wx_tip <0 || $distribution_tip <0 || $platform['amount'] < 0 || $merchant_cash < 0) {
FileLog::error('pay-service: 支付预结算失败,结算资金为负', FileLog::error('pay-service: 支付预结算失败,结算资金为负',
json_encode([$this->order_info['payment'], $wx_tip, $distribution_tip, $platform_tip, $merchant_cash])); json_encode([
'funds'=>$this->pay_order['payment'],
'wx_tip'=>$wx_tip,
'marketing_tip'=>$this->pay_order['pay_discount'],
'distribution_tip'=>$distribution_tip,
'platform_tip'=>$platform['amount'],
'merchant_cash'=>$merchant_cash,
]));
throw new PayException(['cus'=>12]); throw new PayException(['cus'=>12]);
} }
$this->make_merchant_clearing($merchant_cash); //商户收益 $this->make_merchant_clearing($merchant_cash); //商户收益
try { try {
$cnt = true;
PayOrderClearing::beginTransaction(); PayOrderClearing::beginTransaction();
$c_cnt = PayOrderItem::insert($pay_order_items, ['rowCount' => true]);
$i_cnt = PayOrderClearing::insert($this->clear_list, ['rowCount' => true]);
$ci_cnt = PayOrderClearingItem::insert($this->clear_items_list, ['rowCount' => true]);
if ($i_cnt * $c_cnt * $ci_cnt == 0) { //总账资金记账
$cnt = $cnt && FundsService::credit([
'trade_id'=>$this->pay_order['order_id'],
'third_order_id'=>$this->pay_order['third_order_id'],
'amount'=>$this->pay_order['pay_amount'],
'tip'=>$wx_tip,
], 1);
//营销扣款
$cnt = $cnt && AccountService::pay($coupon_items, $this->pay_order['order_id']);
$cnt = $cnt && PayOrderClearing::insert($this->clear_list, ['rowCount' => true]);
$cnt = $cnt && PayOrderClearingItem::insert($this->clear_items_list, ['rowCount' => true]);
if ($cnt == false) {
throw new PayException(['cus' => 0]); throw new PayException(['cus' => 0]);
} }
...@@ -559,34 +760,29 @@ class PayService ...@@ -559,34 +760,29 @@ class PayService
* @return false|float * @return false|float
* 获取微信手续费 * 获取微信手续费
*/ */
private function make_tip_clearing() private function make_tip_clearing($payer)
{ {
$wx_rate = config('pay','pay.wechat_rate') ?? 60; //微信利率 60 / 10000 $account = $payer->clearChannelTips($this->pay_order);
$wx_account_id = config('pay','pay.wechat_account_id') ?? '1111'; if(empty($account)) {
$float_tip = $wx_rate * $this->order_info['payment'] / 10000;
if($float_tip >= 1 ) {
$total_tip = round($float_tip);
} else {
$total_tip = 0;
}
if (empty($total_tip)) {
return 0; return 0;
} }
$account = [
'account_id' => $wx_account_id,
'amount' => $total_tip,
];
$account['account_type'] = 4; //三方手续费 + 平台总帐 $account['account_type'] = 4; //三方手续费 + 平台总帐
$account['pay_type'] = 2; //支付 $account['pay_type'] = 2; //支付
$account['pay_sub_type'] = 406; //第三方渠道手续费 $account['pay_sub_type'] = 406; //第三方渠道手续费
$account['need_recorded'] = 0; //不需同步给平台
/* 微信手续费总单,在退款、核销的时候会更新这个金额,保证他的准确 */ $this->clear_list[] = [
$this->do_clearing_data($account, $total_tip, true); 'pay_order_clearing_id' => array_shift($this->gen_ids),
return $total_tip; 'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['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'],
];
return $account['amount'];
} }
/** /**
...@@ -594,27 +790,56 @@ class PayService ...@@ -594,27 +790,56 @@ class PayService
* @return false|float * @return false|float
* 计算平台收入 * 计算平台收入
*/ */
private function make_platform_clearing() private function make_platform_clearing($payer)
{ {
$account = [ $account = $payer->clearPlatformTips($this->order_info);
'account_id' => '100020003201', if(empty($account)) {
'amount' => 0,
'rate' => 0,
'title' => '平台收益',
];
$total_tip = $account['amount'] = round($this->order_info['total_price'] * $account['rate'] / 10000);
if (empty($total_tip)) {
return 0; return 0;
} }
$account['account_type'] = Dictionary::ACCOUNT_TYPE_SS; $account['account_type'] = 3; //平台(平台收入+平台补贴)
$account['pay_type'] = Dictionary::PAY_TYPE_IN; $account['pay_type'] = 2; //支付
$account['pay_sub_type'] = Dictionary::PAY_SUB_TYPE_SYS_IN; $account['pay_sub_type'] = 405; //平台抽成
$account['need_recorded'] = Dictionary::NO;
$this->do_clearing_data($account, $total_tip, false); $total_amount = $this->pay_order['pay_amount']; //订单支付总价格
return $total_tip; $cleared_amount = 0; //已经结算记录
$cleared_tip = 0; //已经结算记录
$pay_order_clearing_id = array_shift($this->gen_ids);
$this->clear_list[] = [
'pay_order_clearing_id' => $pay_order_clearing_id,
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'account_id' => $account['account_id'],
'account_type' => 3,
'pay_amount' => $account['amount'],
'pay_type' => 2,
'pay_sub_type' => 405,
];
foreach ($this->order_items as $r) {
$current_tip = Strategy::getTip($r['payment'], $total_amount, $cleared_amount, $account['amount'], $cleared_tip);
$this->clear_items_list[] = [
'pay_order_clearing_item_id' => array_shift($this->gen_ids),
'pay_order_clearing_id' => $pay_order_clearing_id,
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'order_item_id' => $r['order_item_id'],
'account_id' => $account['account_id'],
'account_type' => 3,
'pay_amount' => $current_tip,
'clear_payment' => 0, //商品结算价格
'pay_type' => 2,
'pay_sub_type' => 405,
'remark' => '',
];
$cleared_amount += $r['payment'];
$cleared_tip += $current_tip;
}
return $account;
} }
/** /**
...@@ -623,15 +848,14 @@ class PayService ...@@ -623,15 +848,14 @@ class PayService
*/ */
private function make_distribution_clearing() private function make_distribution_clearing()
{ {
/** /**
* 'distributor_user_id'=>122234, * 'distributor_user_id'=>122234,
* 'distributor_commission_value'=>60, * 'distributor_commission_value'=>60,
* 'parent_user_id'=>122234 . rand(111, 999), * 'parent_user_id'=>122234 . rand(111, 999),
* 'parent_commission_value'=>60, * 'parent_commission_value'=>60,
*/ */
$total_tip = 0; $total_tip = 0;
$maps = [];
foreach ($this->order_items as $r) { foreach ($this->order_items as $r) {
//仅处理分销业务 //仅处理分销业务
if(empty($r['distributor_user_id']) || $r['marketing_type'] != 1) { if(empty($r['distributor_user_id']) || $r['marketing_type'] != 1) {
...@@ -640,14 +864,15 @@ class PayService ...@@ -640,14 +864,15 @@ class PayService
$r['distributor_commission_value'] = intval($r['distributor_commission_value']); $r['distributor_commission_value'] = intval($r['distributor_commission_value']);
$r['parent_commission_value'] = intval($r['parent_commission_value']); $r['parent_commission_value'] = intval($r['parent_commission_value']);
$_one = $_two = 0; if($r['commission_mode'] == 1) { //按比例结算
if($r['commission_mode'] == 1) {
$_one = floor($r['distributor_commission_value'] * $this->order_info['total_price'] / 10000); $_one = floor($r['distributor_commission_value'] * $this->order_info['total_price'] / 10000);
$_two = floor($r['parent_commission_value'] * $this->order_info['total_price'] / 10000); $_two = floor($r['parent_commission_value'] * $this->order_info['total_price'] / 10000);
} elseif($r['commission_mode'] == 2) { } elseif($r['commission_mode'] == 2) {//固定金额结算
$_one = $r['distributor_commission_value']; $_one = $r['distributor_commission_value'];
$_two = $r['parent_commission_value']; $_two = $r['parent_commission_value'];
} else { //不处理
continue;
} }
$users[0] = [ $users[0] = [
...@@ -665,13 +890,27 @@ class PayService ...@@ -665,13 +890,27 @@ class PayService
} }
foreach ($users as $u) { foreach ($users as $u) {
if (empty($r['user_id']) || $u['amount'] == 0) { if (empty($u['user_id']) || $u['amount'] == 0) {
continue; continue;
} }
if(!isset($maps[$u['user_id']])) {
$maps[$u['user_id']] = [
'pay_order_clearing_id'=>array_shift($this->gen_ids),
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'account_id' => $u['user_id'],
'account_type' => 1,
'pay_amount' => 0,
'pay_type' => 1,
'pay_sub_type' => 404,
];
}
$maps[$u['user_id']]['pay_amount'] += $u['amount'];
$this->clear_items_list[] = [ $this->clear_items_list[] = [
'pay_order_clearing_item_id' => $this->gen_pay_order_clearing_item_id(), 'pay_order_clearing_item_id' => array_shift($this->gen_ids),
'pay_order_clearing_id' => '', 'pay_order_clearing_id' => $maps[$u['user_id']]['pay_order_clearing_id'],
'pay_order_id' => $this->pay_order['pay_order_id'], 'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'], 'order_id' => $this->pay_order['order_id'],
'order_item_id' => $r['order_item_id'], 'order_item_id' => $r['order_item_id'],
...@@ -680,81 +919,50 @@ class PayService ...@@ -680,81 +919,50 @@ class PayService
'pay_amount' => $u['amount'], 'pay_amount' => $u['amount'],
'clear_payment' => 0, //商品结算价格 'clear_payment' => 0, //商品结算价格
'pay_type' => 1, //收入 'pay_type' => 1, //收入
'pay_sub_type' => 105, //团长佣金收入 'pay_sub_type' => 404, //团长佣金收入
'remark' => '', 'remark' => '',
'need_recorded' => 1,
]; ];
$total_tip += $u['amount']; $total_tip += $u['amount'];
} }
} }
array_push($this->clear_list, array_values($maps));
return $total_tip; return $total_tip;
} }
/** /**
* @param $merchant_cash * @param $merchant_cash
* @return mixed * @return mixed
* 计算商户清分信息 * 计算商户清分信息, 存在结算价则需要二次平台收入
*/ */
private function make_merchant_clearing($merchant_cash) private function make_merchant_clearing($merchant_cash, $platform)
{ {
$ret = $this->get_life_account($this->order_info['life_account_id']); $m = $this->get_merchant_account($this->pay_order['life_account_id'], $this->pay_order['life_account_id']);
if(empty($ret['result']['life_account_admin_id'])) {
$log_string = json_encode([ $merchant['account_id'] = $m['id']; //商户
'life_account_id'=>$this->order_info['life_account_id'], $merchant['account_type'] = 2; //商户
'ret'=>$ret, $merchant['pay_type'] = 1; //收入
], JSON_UNESCAPED_UNICODE); $merchant['pay_sub_type'] = 407; //商品交易102
$merchant['amount'] = 0;
FileLog::error('pay-service:', '获取生活号管理员失败', $log_string);
throw new PayException(['cus'=>13]); $platform['account_type'] = 3;
} $platform['pay_type'] = 1;
$platform['pay_sub_type'] = 408; //平台抽成(结算价);
$account = [ $platform['amount'] = 0;
'account_id' => $ret['result']['life_account_admin_id'],
'amount' => $merchant_cash, $m_clearing_id = array_shift($this->gen_ids); //商家清分ID
'title' => '商家订单收益', $p_clearing_id = array_shift($this->gen_ids); //平台结算价清分ID
];
$total_amount = $this->pay_order['pay_amount']; //订单支付总价格
$account['account_type'] = 2; //商户 $cleared_amount = 0; //已经结算金额记录
$account['pay_type'] = 1; //收入 $cleared_tip = 0; //已经结算tip记录
$account['pay_sub_type'] = 102; //商品交易 $total_tip = $merchant_cash; //已经结算记录
$account['need_recorded'] = 1; //同步到平台总帐
$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->pay_order['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'],
];
if ($is_wx) { //微信不计算手续费拆弹
return true;
}
foreach ($this->order_items as $r) { foreach ($this->order_items as $r) {
$current_tip = Strategy::getTip($r['payment'], $total_amount, $cleared_amount, $total_tip, $cleared_tip); $current_tip = Strategy::getTip($r['payment'], $total_amount, $cleared_amount, $total_tip, $cleared_tip);
$id = $this->gen_pay_order_clearing_item_id();
//商家资金需要显示商品名称 //商家资金需要显示商品名称
if($account['pay_sub_type'] == 102) {
$remark = $r['goods_name'] ?? ''; $remark = $r['goods_name'] ?? '';
if(mb_strlen($remark) > 100) { if(mb_strlen($remark) > 100) {
$remark = mb_substr($remark, 0, 100) . '...'; $remark = mb_substr($remark, 0, 100) . '...';
...@@ -766,97 +974,103 @@ class PayService ...@@ -766,97 +974,103 @@ class PayService
$clear_payment = 0; $clear_payment = 0;
} }
} else { $pay_amount = $current_tip;
$remark = ''; if($current_tip < $clear_payment) {
$clear_payment = 0; throw new PayException(['cus'=>12]); //结算价不能大于商家分成部分
}
}elseif($clear_payment < $current_tip) {
//结算价剩余部分返回平台
$p_tip = $current_tip - $clear_payment;
$platform['pay_amount'] += $p_tip;
$this->clear_items_list[] = [ $this->clear_items_list[] = [
'pay_order_clearing_item_id' => $id, 'pay_order_clearing_item_id' => array_shift($this->gen_ids),
'pay_order_clearing_id' => $pay_order_clearing_id, 'pay_order_clearing_id' => $p_clearing_id,
'pay_order_id' => $this->pay_order['pay_order_id'], 'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'], 'order_id' => $this->pay_order['order_id'],
'order_item_id' => $r['order_item_id'], 'order_item_id' => $r['order_item_id'],
'account_id' => $account['account_id'], 'account_id' => $platform['account_id'],
'account_type' => $account['account_type'], 'account_type' => $platform['account_type'],
'pay_amount' => $current_tip, 'pay_amount' => $p_tip,
'clear_payment' => 0, //商品结算价格
'pay_type' => $platform['pay_type'],
'pay_sub_type' => $platform['pay_sub_type'],
'remark' => '',
];
$pay_amount = $clear_payment;
} else{}
$this->clear_items_list[] = [
'pay_order_clearing_item_id' => array_shift($this->gen_ids),
'pay_order_clearing_id' => $m_clearing_id,
'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'order_item_id' => $r['order_item_id'],
'account_id' => $merchant['account_id'],
'account_type' => $merchant['account_type'],
'pay_amount' => $pay_amount,
'clear_payment' => $clear_payment, //商品结算价格 'clear_payment' => $clear_payment, //商品结算价格
'pay_type' => $account['pay_type'], 'pay_type' => $merchant['pay_type'],
'pay_sub_type' => $account['pay_sub_type'], 'pay_sub_type' => $merchant['pay_sub_type'],
'remark' => $remark, 'remark' => $remark,
'need_recorded' => $account['need_recorded'],
]; ];
$cleared_amount += $r['payment']; $cleared_amount += $r['payment'];
$cleared_tip += $current_tip; $cleared_tip += $current_tip;
} }
}
private function gen_pay_order_id($user_id) //商户收益
{ $this->clear_list[] = [
$number = substr($user_id, -2); 'pay_order_clearing_id' => $m_clearing_id,
$ids = $this->get_idgen_id($number, 1); 'pay_order_id' => $this->pay_order['pay_order_id'],
return array_pop($ids); 'order_id' => $this->pay_order['order_id'],
} 'account_id' => $merchant['account_id'],
'account_type' => $merchant['account_type'],
private function gen_pay_order_item_id() 'pay_amount' => $merchant['amount'],
{ 'pay_type' => $merchant['pay_type'],
$number = substr($this->order_info['user_id'], -2); 'pay_sub_type' => $merchant['pay_sub_type'],
$ids = $this->get_idgen_id($number, 1); ];
return array_pop($ids);
}
private function gen_pay_order_clearing_id() //结算价格收益返回平台
{ if($platform['amount'] > 0) {
$number = substr($this->order_info['user_id'], -2); $this->clear_list[] = [
$ids = $this->get_idgen_id($number, 1); 'pay_order_clearing_id' => $p_clearing_id,
return array_pop($ids); 'pay_order_id' => $this->pay_order['pay_order_id'],
'order_id' => $this->pay_order['order_id'],
'account_id' => $platform['account_id'],
'account_type' => $platform['account_type'],
'pay_amount' => $platform['amount'],
'pay_type' => $platform['pay_type'],
'pay_sub_type' => $platform['pay_sub_type'],
];
} }
private function gen_pay_order_clearing_item_id() return $merchant_cash;
{
$number = substr($this->order_info['user_id'], -2);
$ids = $this->get_idgen_id($number, 1);
return array_pop($ids);
} }
private function get_idgen_id($number, $count = 1) private function get_idgen_id($number, $count = 1)
{ {
$number = substr($number, -2);
$number = intval($number); $number = intval($number);
$res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[ $res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[
"type" => "55c768", "type" => "55c768",
'number' => $number, 'number' => $number,
"count" => $count]]); "count" => $count]]);
$id = $res['id_datetime']['55c768'] ?? []; $this->gen_ids = $res['id_datetime']['55c768'] ?? [];
return $id; return $this->gen_ids;
} }
private function get_life_account($life_account_id) private function get_merchant_account($life_account_id, $shop_id)
{ {
$url = config('interface', 'merchant.lifeaccount.get_life_account_by_id'); $url = config('interface', 'merchant.lifeaccount.get_life_account_by_id');
if (!$url) { if (!$url) {
throw new CodeSpecialException("failed" . __METHOD__); throw new CodeSpecialException("failed" . __METHOD__);
} }
$params = ['life_account_id' => $life_account_id]; $params = ['life_account_id' => $life_account_id];
return Sdk::call($url, $params); $account = Sdk::call($url, $params);
}
private function getChannel($pay_method_id) {
$maps = [ return $account;
105=>"wx", //微信 App 支付
106=>"alipay", //支付宝
107=>"wx_pub", //微信 JSAPI 支付
108=>"wx_pub_qr", //微信 Native 支付
109=>"wx_wap", //微信 H5 支付
110=>"wx_lite", //微信小程序支付
];
if(!empty($maps[$pay_method_id])) {
return $maps[$pay_method_id];
} else {
return $maps[105];
}
} }
private function getNextTime($times = 0) { private function getNextTime($times = 0) {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
namespace App\Services\pay; namespace App\Services\pay;
use App\Exception\custom\FundsException; use App\Exception\custom\FundsException;
use App\Models\order\mysql\PlatformFunds; use App\Models\order\mysql\PlatformAccount;
use App\Models\order\mysql\PlatformFundsLog; use App\Models\order\mysql\PlatformAccountLog;
class FundsService class PlatformAccountService
{ {
/** /**
* @param $data * @param $data
...@@ -17,62 +17,27 @@ class FundsService ...@@ -17,62 +17,27 @@ class FundsService
* @throws FundsException * @throws FundsException
* 渠道账户入账记录 * 渠道账户入账记录
*/ */
public static function credit($data, $channel = 1, $source_name = 10, $service_name = 1) { public static function credit($data, $source_name = 10, $service_name = 1) {
$info = PlatformFunds::getMaster('*', $info = PlatformAccount::getMaster('*',
['channel'=>$channel, 'source_name'=>$source_name, 'service_name'=>$service_name]); ['source_name'=>$source_name, 'service_name'=>$service_name]);
if(empty($info)) {
throw new FundsException(['cus'=>1]);
}
$cnt = PlatformFunds::update([
'total_amount[+]'=>$data['amount'],
'total_tip[+]'=>$data['tip'],
], ['platform_funds_id'=>$info['platform_funds_id']]);
$l_cnt = PlatformFundsLog::insert([
'platform_funds_id'=>$info['platform_funds_id'],
'trade_id'=>$data['order_id'],
'third_order_id'=>$data['third_id'],
'fund_type'=>1,
'amount'=>$data['amount'],
'tip'=>$data['tip'],
], ['rowCount' => true]);
return $l_cnt && $cnt;
}
/**
* @param $data
* @param int $channel
* @param int $source_name
* @param int $service_name
* @throws FundsException
* 渠道账户出账
*/
public static function payOut($data, $channel = 1, $source_name = 10, $service_name = 1) {
$info = PlatformFunds::getMaster('*',
['channel'=>$channel, 'source_name'=>$source_name, 'service_name'=>$service_name]);
if(empty($info)) { if(empty($info)) {
throw new FundsException(['cus'=>1]); throw new FundsException(['cus'=>1]);
} }
$cnt = true; $cnt = true;
$cnt = $cnt && PlatformFunds::update([ $cnt = $cnt && PlatformAccount::update([
'total_amount[-]'=>$data['amount'], 'receivable_amount[+]'=>$data['amount']
'total_tip[-]'=>$data['tip'],
], ['platform_funds_id'=>$info['platform_funds_id']]); ], ['platform_funds_id'=>$info['platform_funds_id']]);
$cnt = $cnt && PlatformFundsLog::insert([ $cnt = $cnt && PlatformAccountLog::insert([
'platform_funds_id'=>$info['platform_funds_id'], 'platform_account_id'=>$info['platform_account_id'],
'trade_id'=>$data['order_id'], 'trade_id'=>$data['trade_id'],
'third_order_id'=>$data['third_id'], 'fund_type'=>1,
'fund_type'=>2, 'amount'=>$data['amount']
'amount'=>$data['amount'],
'tip'=>$data['tip'],
], ['rowCount' => true]); ], ['rowCount' => true]);
return $cnt; return $cnt;
} }
} }
\ No newline at end of file
...@@ -4,17 +4,21 @@ ...@@ -4,17 +4,21 @@
namespace App\Services\refund; namespace App\Services\refund;
use Api\PhpServices\Idgen\Idgen; use Api\PhpServices\Idgen\Idgen;
use Api\PhpUtils\Log\FileLog;
use App\Exception\BaseException; use App\Exception\BaseException;
use App\Models\Dictionary;
use App\Models\order\mysql\PayOrder; use App\Models\order\mysql\PayOrder;
use App\Models\order\mysql\PayOrderItem;
use App\Models\order\mysql\PayCouponItem;
use App\Models\order\mysql\PayOrderClearing; use App\Models\order\mysql\PayOrderClearing;
use App\Models\order\mysql\PayOrderClearingItem; use App\Models\order\mysql\PayOrderClearingItem;
use App\Models\order\mysql\PayOrderItem; use App\Models\order\mysql\RefundMerchantRecord;
use App\Models\order\mysql\RefundOrder; use App\Models\order\mysql\RefundOrder;
use App\Services\order\OrderService; use App\Models\order\mysql\RefundOrderItem;
use App\Models\order\mysql\RefundPlatformRecord;
use App\Services\Channel;
use App\Services\marketing\AccountService;
use App\Services\pingxx\PingxxService; use App\Services\pay\FundsService;
use Helpers\Strategy; use Helpers\Strategy;
use App\Exception\custom\RefundException; use App\Exception\custom\RefundException;
...@@ -24,9 +28,10 @@ class RefundService ...@@ -24,9 +28,10 @@ class RefundService
{ {
/** /**
* @param $order_item_id * @param $order_id
* @param $user_id * @param array $order_item_ids
* @throws RefundException * @return array
* @throws BaseException
* 发起退款请求 * 发起退款请求
*/ */
public function do_refund($order_id, $order_item_ids = []) { public function do_refund($order_id, $order_item_ids = []) {
...@@ -37,43 +42,17 @@ class RefundService ...@@ -37,43 +42,17 @@ class RefundService
$data = $this->can_refund($order_id, $order_item_ids); $data = $this->can_refund($order_id, $order_item_ids);
/* $mata_data = [
https://www.pingxx.com/api/%E8%AE%A2%E5%8D%95%20Refunds%20%E9%80%80%E6%AC%BE%E6%A6%82%E8%BF%B0.html
订单退款 paid = true, status = canceled, amount_paid > 0 则表明可以退款
amount_paid 大于 actual_amount 需要最后一单全退。
具体见ping++ 文档
*/
$pay = PingxxService::getInstance()->getOrder($data['pay_order']['third_order_id']);
if(empty($pay['paid']) || $pay['paid'] != true) {
throw new RefundException(['cus' => 12]);
}
// 存在多次支付的场景需要人工介入处理, 修复订单支付金额,退款金额
if($pay['amount_paid'] > $pay['actual_amount']) {
FileLog::error('pay-service: 订单可能存在多次成功支付,需人工确认处理。', json_encode($pay, JSON_UNESCAPED_UNICODE));
throw new RefundException(['cus' => 13]);
}
// 构造退款数据
$refund = [
'id'=>$pay['id'], //pingxx订单ID
'charge'=>$pay['charge'], //pingxx 支付订单ID
'refund_mode' => 'to_source', //默认返回
'charge_amount'=>$data['refund_order']['refund_amount'],
'metadata' => [
'app_id'=>'merchant-c', 'app_id'=>'merchant-c',
'pingxx_id'=>PingxxService::getInstance()->getAppId(),
'environ'=>Application::app()->environ(), 'environ'=>Application::app()->environ(),
'refund_order_id'=>$data['refund_order']['refund_order_id'], 'refund_order_id'=>$data['refund_order']['refund_order_id'],
'order_item_id'=>$data['order_item_id'], 'order_item_id'=>$data['order_item_id'],
'order_id'=>$order_id, 'order_id'=>$order_id,
],
]; ];
try{ try{
$ret = PingxxService::getInstance()->sendRefund($refund); $payer = Channel::getChannel($data['pay_order']['pay_channel']);
$ret = $payer->refund($data, $mata_data);
if(!empty($ret["data"])) { if(!empty($ret["data"])) {
$edit = ['request_pingxx_success_time'=>date('Y-m-d H:i:s')]; $edit = ['request_pingxx_success_time'=>date('Y-m-d H:i:s')];
RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]); RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]);
...@@ -103,7 +82,7 @@ class RefundService ...@@ -103,7 +82,7 @@ class RefundService
*/ */
public function call_back($data) { public function call_back($data) {
if (!in_array($data['type'], ['refund.succeeded', 'order.refunded'])) { if (!in_array($data['type'], ['refund.succeeded'])) {
return ['error'=>'退款回调类型不正确']; return ['error'=>'退款回调类型不正确'];
} }
...@@ -117,15 +96,7 @@ class RefundService ...@@ -117,15 +96,7 @@ class RefundService
return ['error'=>'非merchant渠道订单,不做处理。']; return ['error'=>'非merchant渠道订单,不做处理。'];
} }
if($data['type'] == 'refund.succeeded') { if($object['succeed'] != true) {
$refund_order_id = $object['order_no'];
$refunded = $object['succeed']; //"succeed": true,
} else {
$refund_order_id = $object['merchant_order_no'];
$refunded = $object['refunded'];
}
if($refunded != true) {
return ['error'=>'退款状态不正确,不做处理。']; return ['error'=>'退款状态不正确,不做处理。'];
} }
...@@ -137,63 +108,151 @@ class RefundService ...@@ -137,63 +108,151 @@ class RefundService
return ['error'=>'退款订单缺少order_item_id,不做处理。']; return ['error'=>'退款订单缺少order_item_id,不做处理。'];
} }
/**
平台出账、手续费返还。
营销补贴返还
团长收入减少
平台收入减少
商户收入减少
*/
// 修改状态,锁定业务 // 修改状态,锁定业务
$refund_order_id = $object['metadata']['refund_order_id'];
RefundOrder::beginTransaction(); RefundOrder::beginTransaction();
$refund = RefundOrder::getMaster('*', ['refund_order_id'=>$refund_order_id]);
$cnt = true;
//营销退款处理
$coupons = PayCouponItem::selectMaster('*',
['order_item_id'=>$object['metadata']['order_item_id']]);
if($coupons) {
$cnt = $cnt && AccountService::refund($coupons, $refund_order_id);
}
//总账资金记账
$cnt = $cnt && FundsService::payOut([
'trade_id'=>$refund['refund_order_id'],
'third_order_id'=>$object['id'],
'amount'=>$refund['refund_amount'],
'tip'=>$refund['refund_wx_tip'],
], 1);
$refund_order_id = $object['metadata']['refund_order_id'];
$edit = [ $edit = [
'refund_order_status'=>2, //pingxx回调成功, 'refund_order_status'=>2, //pingxx回调成功,
'pingxx_callback_success_time'=>date('Y-m-d H:i:s'), //回调成功时间 'pingxx_callback_success_time'=>date('Y-m-d H:i:s'), //回调成功时间
]; ];
$cnt_r = RefundOrder::update($edit, ['refund_order_id'=>$refund_order_id, 'refund_order_status'=>1]); $cnt = $cnt && RefundOrder::update($edit, ['refund_order_id'=>$refund_order_id, 'refund_order_status'=>1]);
$cnt_i = PayOrderItem::update(['refund_order_status'=>2], [ $cnt = $cnt && PayOrderItem::update(['refund_order_status'=>2], [
'order_item_id'=>$object['metadata']['order_item_id'] 'order_item_id'=>$object['metadata']['order_item_id']
]); ]);
if($cnt_r && $cnt_i) { $cnt = $cnt && $this->refund_clear_record($refund, $object['metadata']['order_item_id']);
if($cnt) {
RefundOrder::commit(); RefundOrder::commit();
return ['refund_order_id'=>$refund_order_id];
} else { } else {
RefundOrder::rollback(); RefundOrder::rollback();
throw new RefundException(['cus' => 16]);
}
} }
$refund = RefundOrder::getMaster('*', ['refund_order_id'=>$refund_order_id]); /**
* @param $refund
* @param array $order_item_ids
* @return bool
*/
private function refund_clear_record($refund, $order_item_ids = []) {
$clear_items = PayOrderClearingItem::selectMaster('*',
['order_item_id'=>$order_item_ids]);
$ids = $this->get_idgen_id($refund['user_id'], count($clear_items) * 4);
$d = $m = $p = [];
foreach ($clear_items as $r) {
if($r['pay_sub_type'] == 404) { // 团长佣金收入
if(!isset($d[$r['account_id']])) {
$d[$r['account_id']] = [
'refund_personal_id'=>array_shift($ids),
'refund_order_id'=>$refund['refund_order_id'],
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'user_id'=>$r['account_id'],
'payment'=>0,
];
}
$d[$r['account_id']]['payment'] += $r['pay_amount'];
} elseif ($r['pay_sub_type'] == 405 || $r['pay_sub_type'] == 408) { // 平台抽成
if(!isset($p[$r['account_id']])) {
$p[$r['account_id']] = [
'refund_platform_id'=>array_shift($ids),
'refund_order_id'=>$refund['refund_order_id'],
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'account_id'=>$r['account_id'],
'payment'=>0,
];
}
$p[$r['account_id']]['payment'] += $r['pay_amount'];
} elseif ($r['pay_sub_type'] == 407) { // 商品交易
if(!isset($m[$r['account_id']])) {
$m[$r['account_id']] = [
'refund_merchant_id'=>array_shift($ids),
'refund_order_id'=>$refund['refund_order_id'],
'pay_order_id'=>$r['pay_order_id'],
'order_id'=>$r['order_id'],
'account_id'=>$r['account_id'],
'payment'=>0,
];
}
$m[$r['account_id']]['payment'] += $r['pay_amount'];
if($refund['refund_order_status'] != 2) {
throw new RefundException(['cus' => 16]); } else {}
} }
return ['refund_order_id'=>$refund_order_id]; $cnt = true;
$cnt = $cnt && RefundMerchantRecord::insert(array_values($m), ['rowCount' => true]);
if($p) {
$cnt = $cnt && RefundPlatformRecord::insert(array_values($p), ['rowCount' => true]);
}
if($d) {
$cnt = $cnt && RefundPlatformRecord::insert(array_values($d), ['rowCount' => true]);
}
//不用搞清分核算,是否需要平台记账 return $cnt;
} }
/** /**
* @param $where * @param $order_id
* @return \Api\PhpUtils\Mysql\MysqlBase|array * @param $order_item_ids
* @throws RefundException * @return array
* @throws BaseException
* 判断订单是否可退,判断是否存在退款申请 * 判断订单是否可退,判断是否存在退款申请
*/ */
private function can_refund($order_id, $order_item_ids) { private function can_refund($order_id, $order_item_ids = []) {
/** /**
* 业务方自行解决,订单关闭、订单超过时长不允许退款等逻辑 * 业务方自行解决,订单关闭、订单超过时长不允许退款等逻辑
* 支付中心退款判断仅做主单是否支付,主单是否支持退款的判断 * 支付中心退款判断仅做主单是否支付,主单是否支持退款的判断
*/ */
$refund_payment = 0; $refund_payment = 0;
try{ try{
RefundOrder::beginTransaction(); RefundOrder::beginTransaction();
$pay_order = PayOrder::getMaster('*', ['order_id'=>$order_id]); $pay_order = PayOrder::getMaster('*', ['order_id'=>$order_id]);
if(empty($pay_order)) { if(empty($pay_order) || $pay_order['pay_order_status'] != 2) {
throw new RefundException(['cus' => 15]); throw new RefundException(['cus' => 2]);
}
$pay_order_items = PayOrderItem::selectMaster('*', ['order_id'=>$order_id]);
if(empty($pay_order_items)) {
throw new RefundException(['cus' => 4]);
} }
$pay_order_items = PayOrderItem::selectMaster('*', ['pay_order_id'=>$pay_order['pay_order_id']]);
foreach ($pay_order_items as $item) { foreach ($pay_order_items as $item) {
if($item['can_notify_account'] == 1) { if($item['can_notify_account'] == 1) {
continue; //已结算不允许退款 continue; //已结算不允许退款
...@@ -208,12 +267,11 @@ class RefundService ...@@ -208,12 +267,11 @@ class RefundService
} }
$items[] = $item; $items[] = $item;
$refund_payment += $item['payment']; $refund_payment += $item['payment'];
} }
if(empty($items)) { if(empty($items)) {
throw new RefundException(['cus' => 14]); // throw new RefundException(['cus' => 3]);
} }
//重新计算微信手续费 //重新计算微信手续费
...@@ -222,7 +280,7 @@ class RefundService ...@@ -222,7 +280,7 @@ class RefundService
// 计算已退金额、已退手续费 sum(refund_wx_tip), sum(refund_amount) // 计算已退金额、已退手续费 sum(refund_wx_tip), sum(refund_amount)
$list = RefundOrder::selectMaster('*', ['order_id'=>$pay_order['order_id']]); $list = RefundOrder::selectMaster('*', ['order_id'=>$pay_order['order_id']]);
foreach ($list as $r) { foreach ($list as $r) {
if($r['refund_order_status'] == Dictionary::REFUND_ORDER_STATUS_UNDO) { if($r['refund_order_status'] == 0) {
continue; continue;
} }
...@@ -244,8 +302,10 @@ class RefundService ...@@ -244,8 +302,10 @@ class RefundService
$wx_tip = Strategy::getWxTip($refund_payment, $pay_order['pay_amount'], $cleared_amount, $wx_total_tip, $cleared_tip); $wx_tip = Strategy::getWxTip($refund_payment, $pay_order['pay_amount'], $cleared_amount, $wx_total_tip, $cleared_tip);
$ids = $this->get_idgen_id($pay_order['user_id'], count($items) + 1);
$refund_order_id = array_shift($ids);
$refund = [ $refund = [
'refund_order_id'=>$this->gen_refund_order_id($pay_order['user_id']), 'refund_order_id'=>$refund_order_id,
'user_id'=>$pay_order['user_id'], 'user_id'=>$pay_order['user_id'],
'order_id'=>$pay_order['order_id'], 'order_id'=>$pay_order['order_id'],
'refund_order_status'=>1, 'refund_order_status'=>1,
...@@ -255,46 +315,47 @@ class RefundService ...@@ -255,46 +315,47 @@ class RefundService
'extra'=> json_encode(['order_item_id'=>array_column($items, 'order_item_id')]), 'extra'=> json_encode(['order_item_id'=>array_column($items, 'order_item_id')]),
]; ];
$cnt = RefundOrder::insert($refund, ['rowCount'=>true]); $refund_items = [];
if($cnt == 0) { foreach ($items as $r) {
throw new RefundException(['cus' => 0]); $refund_items[] = [
'refund_order_item_id'=>array_shift($ids),
'refund_order_id'=>$refund_order_id,
'order_item_id'=>$r['order_item_id'],
];
} }
//批量更新子单状态 "user_id" => [2, 123, 234, 54], $cnt = true;
PayOrderItem::update(['refund_order_status'=>1], ['order_item_id'=>array_column($items, 'order_item_id')]); $cnt = $cnt && RefundOrder::insert($refund, ['rowCount'=>true]);
$cnt = $cnt && RefundOrderItem::insert($refund_items, ['rowCount'=>true]);
$cnt = $cnt && PayOrderItem::update(['refund_order_status'=>1], ['order_item_id'=>array_column($items, 'order_item_id')]);
$data = [ if($cnt == false) {
'refund_order'=>$refund, throw new RefundException(['cus' => 4]);
'pay_order'=>$pay_order, }
'order_item_id'=>array_column($items, 'order_item_id'),
];
RefundOrder::commit(); RefundOrder::commit();
}catch (\PDOException $e) { }catch (\Exception $e) {
RefundOrder::rollback(); RefundOrder::rollback();
throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]); throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]);
} }
return $data; return [
} 'refund_order'=>$refund,
'pay_order'=>$pay_order,
private function gen_refund_order_id($user_id) 'order_item_id'=>array_column($items, 'order_item_id'),
{ ];
$number = substr($user_id, -2);
$ids = $this->get_idgen_id($number, 1);
return array_pop($ids);
} }
private function get_idgen_id($number, $count = 1) private function get_idgen_id($number, $count = 1)
{ {
$number = substr($number, -2);
$number = intval($number); $number = intval($number);
$res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[ $res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[
"type" => "55c768", "type" => "55c768",
'number' => $number, 'number' => $number,
"count" => $count]]); "count" => $count]]);
$id = $res['id_datetime']['55c768'] ?? []; return $res['id_datetime']['55c768'] ?? [];
return $id;
} }
} }
\ No newline at end of file
...@@ -7,7 +7,7 @@ use App\Exception\custom\CodeSpecialException; ...@@ -7,7 +7,7 @@ use App\Exception\custom\CodeSpecialException;
use Helpers\Sdk; use Helpers\Sdk;
class WalletService class MerchantAccountService
{ {
/** /**
...@@ -16,12 +16,15 @@ class WalletService ...@@ -16,12 +16,15 @@ class WalletService
* @throws CodeSpecialException * @throws CodeSpecialException
* 钱包记录同步 * 钱包记录同步
*/ */
public static function send($params) { public static function sync($params) {
/*
$url = config('interface', 'wallet.account.transfer'); ///account/billrecord/create
$url = config('interface', 'account.account.transfer');
if (!$url) { if (!$url) {
throw new CodeSpecialException("failed"); throw new CodeSpecialException("failed");
} }
*/
$url = 'http://account.dev.yidian-inc.com/account/billrecord/create';
// 服务增加签名 // 服务增加签名
$appid = \Yaf\Application::app()->getConfig()->get('wallet.appid'); $appid = \Yaf\Application::app()->getConfig()->get('wallet.appid');
......
...@@ -38,4 +38,5 @@ class WalletService ...@@ -38,4 +38,5 @@ class WalletService
$sign = md5($params['time'] . $secret); $sign = md5($params['time'] . $secret);
return $sign; return $sign;
} }
} }
\ No newline at end of file
<?php <?php
var_dump($_POST);
$ids = ["21092214200312204001","21092214200312204002","21092214200312204003","21092214200312204004","21092214200312204005"];
$t = array_shift($ids);
var_dump($t);
\ 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