汪总电商平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1185 lines
45 KiB

<?php
namespace addons\wanlshop\library\WanlPay;
use app\common\library\Auth;
use addons\wanlshop\library\WanlChat\WanlChat;
use addons\wanlshop\library\EasyWeChat\Easywechat;
use think\Db;
use think\Request;
use think\Exception;
use think\exception\PDOException;
use think\exception\ValidateException;
use fast\Random;
use WanlPay\Yansongda\Pay;
use WanlPay\Yansongda\Log;
use WanlPay\Supports\Str;
/**
*
* WanlPay 多终端支付
* @author 深圳前海万联科技有限公司 <wanlshop@i36k.com>
* @link http://www.wanlshop.com
*
* @careful 非FastAdmin购买授权,未经版权所有权人书面许可,不得自行复制到第三方系统使用、二次开发、分支、复制和商业使用!
* @creationtime 2020年8月7日23:46:12
* @lasttime 2021年9月27日00:48:32
* @version V2.x https://pay.yansongda.cn/docs/v2/
**/
class WanlPay
{
private $type;
private $method;
private $code;
public function __construct($type = '' ,$method = '', $code = '')
{
$auth = Auth::instance(); // 方式
$this->wanlchat = new WanlChat();
$this->type = $type; // 类型
$this->method = $method; // 方式
$this->user_id = $auth->isLogin() ? $auth->id : 0; // 用户ID
$this->request = \think\Request::instance();
$this->code = $code; // 小程序code
}
/**
* 支付
*/
public function pay($order_id, $order_type)
{
if($this->user_id == 0){
return ['code' => 10001 ,'msg' => '用户ID不存在'];
}
// 获取支付信息
$pay = model('app\api\model\wanlshop\Pay')
->where('order_id', 'in', $order_id)
->where(['user_id' => $this->user_id, 'type' => $order_type == 'groups' ? 'groups' : 'goods'])
->select();
// 1.0.8 升级
$price = 0; // 付款金额
$order_no = []; // 订单号
$pay_id = []; // 交易号
foreach($pay as $row){
$price = bcadd($price, $row['price'], 2); // 总价格
$order_no[] = $row['order_no']; // 订单集
$pay_id[] = $row['id']; // 交易集
}
// 第三方支付订单 1.0.8
if($this->type != 'balance'){
$payOutTrade = model('app\api\model\wanlshop\PayOutTrade');
$payOutTrade->out_trade_no = date('YmdHis'). rand(10000000,99999999). substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
$payOutTrade->pay_id = $pay_id;
$payOutTrade->order_id = $order_id;
$payOutTrade->price = $price;
$payOutTrade->save();
}
// 1.0.8升级 拼团订单
if($order_type == 'groups'){
$title = '拼团订单-' . implode("_", $order_no);
$model_order = model('app\api\model\wanlshop\groups\Order');
$model_order_goods = model('app\api\model\wanlshop\groups\OrderGoods');
$model_goods = model('app\api\model\wanlshop\groups\Goods');
}else{
$title = '商城订单-' . implode("_", $order_no);
$model_order = model('app\api\model\wanlshop\Order');
$model_order_goods = model('app\api\model\wanlshop\OrderGoods');
$model_goods = model('app\api\model\wanlshop\Goods');
}
// 支付方式
if ($this->type == 'balance') {
// 支付列表
$pay_list = [];
// 订单列表
$order_list = [];
// 拼团列表
$groups_list = [];
// 拼团列表
$groups_team_list = [];
// 判断金额
$user = model('app\common\model\User')->get($this->user_id);
if (!$user || $user['money'] < $price) {
return ['code' => 500 ,'msg' => '余额不足本次支付'];
}
$result = false;
$balance_no = date('YmdHis') . rand(10000000,99999999);
Db::startTrans();
try {
foreach($pay as $row){
$isAlone = false; // 拼团订单一般是单订单,暂可以这样操作
$groups_state = 'start'; // 拼团状态
foreach($model_order_goods->where('order_id', $row['order_id'])->select() as $goods){
if($order_type == 'groups' && !empty($goods['group_type'])){
if($goods['group_type'] == 'alone'){
$isAlone = true;
}else{
// 查询团ID
$groups = model('app\api\model\wanlshop\groups\Groups')
->where(['group_no' => $goods['group_no']])
->find();
// 判断是否超团
$groups_team = model('app\api\model\wanlshop\groups\Team')
->where(['group_no' => $goods['group_no']])
->select();
// 已拼团总数量
$groups_team_count = count($groups_team);
if($groups_team_count >= $groups['people_num'] || $groups['join_num'] >= $groups['people_num']){
$this->error(__('参与拼单失败,拼团已完成'));
}
// 判断是否具备成团条件
if(($groups['people_num'] - $groups_team_count) <= 1 || ($groups['people_num'] - $groups['join_num']) <= 1){
$groups_state = 'success';
//调整其他拼团订单
// 订单状态:1=待支付,2=待成团,3=待发货,4=待收货,5=待评论,6=已完成,7=已取消
foreach($groups_team as $team){
$order_list[] = ['id' => $team['order_id'], 'state' => 3, 'groupstime' => time()];
}
}
// 拼团状态: ready=准备中,start=拼团中,success=已成团,fail=拼团失败,auto=自动成团
$groups_list[] = [
'id' => $groups['id'],
'join_num' => $groups['join_num'] + 1,
'state' => $groups_state,
'grouptime' => $groups_state === 'success' ? time() : NULL
];
$groups_team_list[] = [
'user_id' => $user['id'],
'shop_id' => $goods['shop_id'],
'group_no' => $goods['group_no'],
'order_id' => $goods['order_id'],
'order_goods_id' => $goods['id']
];
}
}
// 新增付款人数、新增销量
$model_goods->where('id', $goods['goods_id'])->inc('payment')->inc('sales', $goods['number'])->update();
}
// 订单列表
if($groups_state === 'success'){
$order_list[] = ['id' => $row['order_id'], 'state' => 3, 'paymenttime' => time(), 'groupstime' => time()];
}else{
$order_list[] = ['id' => $row['order_id'], 'state' => $isAlone ? 3 : 2, 'paymenttime' => time()];
}
// 支付列表
$pay_list[] = [
'id' => $row['id'],
'trade_no' => $balance_no, // 第三方交易号
'pay_type' => 0, // 支付类型:0=余额支付,1=微信支付,2=支付宝支付
'pay_state' => 1, // 支付状态 (支付回调):0=未支付,1=已支付,2=已退款
'total_amount' => $price, // 总金额
'actual_payment' => $row['price'], // 实际支付
'notice' => json_encode([
'type' => $this->type,
'user_id' => $user['id'],
'trade_no' => $balance_no,
'out_trade_no' => $row['pay_no'],
'amount' => $row['price'],
'total_amount' => $price,
'order_id' => $row['order_id'],
'trade_type' => 'BALANCE',
'version' => '1.0.0'
])
];
}
// 更新支付列表
$result = model('app\api\model\wanlshop\Pay')->saveAll($pay_list);
// 更新订单列表
$result = $model_order->saveAll($order_list);
// 修改用户金额
if(count($order_no) == 1){
$result = self::money(-$price, $user['id'], '余额支付'.$order_type == 'groups' ? '拼团':'商品'.'订单', $order_type == 'groups' ? 'groups':'pay', implode(",",$order_no));
}else{
$result = self::money(-$price, $user['id'], '余额合并支付'.$order_type == 'groups' ? '拼团':'商品'.'订单', $order_type == 'groups' ? 'groups':'pay', implode(",",$order_no));
}
if($order_type == 'groups'){
model('app\api\model\wanlshop\groups\Groups')->isUpdate()->saveAll($groups_list);
model('app\api\model\wanlshop\groups\Team')->saveAll($groups_team_list);
}
Db::commit();
} catch (Exception $e) {
Db::rollback();
return ['code' => 10002 ,'msg' => $e->getMessage()];
}
// 返回结果
if ($result !== false) {
return ['code' => 200 ,'msg' => '成功', 'data' => []];
} else {
return ['code' => 10003 ,'msg' => '支付异常'];
}
// 支付宝支付、更新数据均在回调中执行
} else if($this->type == 'alipay'){
$data = [
'out_trade_no' => $payOutTrade->out_trade_no,
'total_amount' => $price,
'subject' => $title
];
try{
$alipay = Pay::alipay(self::getConfig($this->type))->{$this->method}($data);
if($this->method == 'app' || $this->method == 'wap'){
return ['code' => 200 ,'msg' => '成功', 'data' => $alipay->getContent()];
}else{
return ['code' => 200 ,'msg' => '成功', 'data' => $alipay];
}
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 微信支付
} else if($this->type == 'wechat'){
$data = [
'out_trade_no' => $payOutTrade->out_trade_no, // 订单号
'body' => $title, // 标题
'total_fee' => $price * 100 //付款金额 单位分
];
if($this->method == 'miniapp'){
// 1.1.9升级 改为Easywechat
try{
$auth = Easywechat::app()
->auth
->session($this->code);
} catch (\Exception $e) {
return ['code' => 10005 ,'msg' => $e->getMessage()];
}
if(isset($auth['errcode'])){
return ['code' => 10005 ,'msg' => $auth['errmsg']];
}
$time = time();
$third = model('app\api\model\wanlshop\Third')
->get(['platform' => 'weixin_open', 'openid' => $auth['openid']]);
if(!$third){
$third = model('app\api\model\wanlshop\Third');
// array_key_exists("unionid",$auth)
if(isset($auth['unionid'])){
$third->unionid = $auth['unionid'];
$third->openid = $auth['openid'];
}else{
$third->openid = $auth['openid'];
}
$third->access_token = $auth['session_key'];
$third->expires_in = 7776000;
$third->logintime = $time;
$third->expiretime = $time + 7776000;
$third->user_id = $this->user_id;
$third->save();
}
$data['openid'] = $auth['openid'];
}else if($this->method == 'mp'){
// 微信公众平台支付必有OPENID 1.1.2升级
$third = model('app\api\model\wanlshop\Third')
->where(['platform' => 'weixin_h5', 'user_id' => $this->user_id])
->find();
if ($third) {
$data['openid'] = $third['openid'];
}else{
return ['code' => 10005 ,'msg' => '获取微信openid失败,无法支付'];
}
}
// 开始支付
try{
$wechat = Pay::wechat(self::getConfig($this->type))->{$this->method}($data);
if($this->method == 'app'){
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat->getContent()];
}else if($this->method == 'wap'){
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat->getTargetUrl()];
}else{
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat];
}
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 百度支付
} else if($this->type == 'baidu'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// QQ支付
} else if($this->type == 'qq'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 苹果支付
} else if($this->type == 'apple'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
} else if($this->type == 'dinpay'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
} else {
return ['code' => 10010 ,'msg' => '未找到任何有效支付'];
}
}
/**
* 用户充值
*/
public function recharge($price)
{
if($this->user_id == 0){
return ['code' => 10001 ,'msg' => '用户ID不存在'];
}
if($price <= 0){
return ['code' => 10002 ,'msg' => '充值金额不合法'];
}
// 充值订单号
$pay_no = date("Ymdhis") . sprintf("%08d", $this->user_id) . mt_rand(1000, 9999);
// 支付标题
$title = '充值-' . $pay_no;
// 生成一个订单
$order = \app\api\model\wanlshop\RechargeOrder::create([
'orderid' => $pay_no,
'user_id' => $this->user_id,
'amount' => $price,
'payamount' => 0,
'paytype' => $this->type,
'ip' => $this->request->ip(),
'useragent' => substr($this->request->server('HTTP_USER_AGENT'), 0, 255),
'status' => 'created'
]);
if($this->type == 'alipay'){
// 获取配置
$payConfig = self::getConfig($this->type);
$payConfig['notify_url'] = str_replace("notify", "notify_recharge", $payConfig['notify_url']);
$data = [
'out_trade_no' => $pay_no,
'total_amount' => $price,
'subject' => $title
];
try{
$alipay = Pay::alipay($payConfig)->{$this->method}($data);
if($this->method == 'app' || $this->method == 'wap'){
return ['code' => 200 ,'msg' => '成功', 'data' => $alipay->getContent()];
}else{
return ['code' => 200 ,'msg' => '成功', 'data' => $alipay];
}
// 1.1.5升级 捕获异常
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 微信支付
} else if($this->type == 'wechat'){
// 获取配置
$payConfig = self::getConfig($this->type);
$payConfig['notify_url'] = str_replace("notify", "notify_recharge", $payConfig['notify_url']);
$data = [
'out_trade_no' => $pay_no, // 订单号
'body' => $title, // 标题
'total_fee' => $price * 100 //付款金额 单位分
];
if($this->method == 'miniapp'){
// 1.1.9升级 获取微信openid改为Easywechat
try{
$auth = Easywechat::app()
->auth
->session($this->code);
} catch (\Exception $e) {
return ['code' => 10005 ,'msg' => $e->getMessage()];
}
if(isset($auth['errcode'])){
return ['code' => 10005 ,'msg' => $auth['errmsg']];
}
$time = time();
$third = model('app\api\model\wanlshop\Third')
->get(['platform' => 'weixin_open', 'openid' => $auth['openid']]);
if(!$third){
$third = model('app\api\model\wanlshop\Third');
if(isset($auth['unionid'])){
$third->unionid = $auth['unionid'];
$third->openid = $auth['openid'];
}else{
$third->openid = $auth['openid'];
}
$third->access_token = $auth['session_key'];
$third->expires_in = 7776000;
$third->logintime = $time;
$third->expiretime = $time + 7776000;
$third->user_id = $this->user_id;
$third->save();
}
$data['openid'] = $auth['openid'];
}else if($this->method == 'mp'){
// 微信公众平台支付必有OPENID 1.1.2升级
$third = model('app\api\model\wanlshop\Third')
->where(['platform' => 'weixin_h5', 'user_id' => $this->user_id])
->find();
if ($third) {
$data['openid'] = $third['openid'];
}else{
return ['code' => 10005 ,'msg' => '获取微信openid失败,无法支付'];
}
}
// 开始支付
try{
$wechat = Pay::wechat($payConfig)->{$this->method}($data);
if($this->method == 'app'){
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat->getContent()];
}else if($this->method == 'wap'){
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat->getTargetUrl()];
}else{
return ['code' => 200 ,'msg' => '成功', 'data' => $wechat];
}
// 1.1.5升级 捕获异常
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 百度支付
} else if($this->type == 'baidu'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// QQ支付
} else if($this->type == 'qq'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
// 苹果支付
} else if($this->type == 'apple'){
try{
} catch (\Exception $e) {
return ['code' => 10006 ,'msg' => $this->type.':'.$e->getMessage()];
}
} else {
return ['code' => 10010 ,'msg' => '未找到任何有效支付'];
}
}
/**
* 支付回调
*/
public function notify()
{
$wanlpay = Pay::{$this->type}(self::getConfig($this->type));
try{
$result = $wanlpay->verify();
// 查询支付集
$payOutTrade = model('app\api\model\wanlshop\PayOutTrade')
->where(['out_trade_no' => $result['out_trade_no']])
->find();
// 查询订单是否存在
$pay = model('app\api\model\wanlshop\Pay')
->where('id', 'in', $payOutTrade['pay_id'])
->where('pay_state', 'neq', '1')
->select();
if(!$pay){
return ['code' => 10001 ,'msg' => '网络异常'];
}
// 1.0.8升级 拼团订单
$order_type = $pay[0]['type'];
if($order_type == 'groups'){
$model_order = model('app\api\model\wanlshop\groups\Order');
$model_order_goods = model('app\api\model\wanlshop\groups\OrderGoods');
$model_goods = model('app\api\model\wanlshop\groups\Goods');
}else{
$model_order = model('app\api\model\wanlshop\Order');
$model_order_goods = model('app\api\model\wanlshop\OrderGoods');
$model_goods = model('app\api\model\wanlshop\Goods');
}
$trade_no = '';
// 支付类型
$pay_type = 8;
$user_id = 0;
$order_no = [];
// 总价格
$price = 0;
// 主要用于日志
foreach($pay as $row){
// $price += $row['price']; 1.0.8 升级
$price = bcadd($price, $row['price'], 2); // 总价格
// 订单集
$order_no[] = $row['order_no'];
$user_id = $row['user_id'];
}
// -----------------------------判断订单是否合法-----------------------------
$config = get_addon_config('wanlshop');
// 支付宝
if ($this->type == 'alipay') {
// 判断状态
if (in_array($result['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
// 判断金额
if($price != $result['total_amount']){
return ['code' => 10002 ,'msg' => '支付金额不合法'];
}
// 判断appid
if($config['sdk_alipay']['app_id'] != $result['app_id']){
return ['code' => 10003 ,'msg' => 'APPID不合法'];
}
}else{
return ['code' => 500 ,'msg' => '支付回调失败'];
}
// 回调支付
$pay_type = 2; // 支付类型:0=余额支付,1=微信支付,2=支付宝支付
$pay_name = '支付宝';
$trade_no = $result['trade_no'];
} else if($this->type == 'wechat'){
// 判断状态
if ($result['result_code'] == 'SUCCESS') {
// 判断金额
if($price != ($result['total_fee'] / 100)){
return ['code' => 10002 ,'msg' => '支付金额不合法'];
}
// 判断商家ID
if($config['sdk_qq']['mch_id'] != $result['mch_id']){
return ['code' => 10004 ,'msg' => '商户不合法'];
}
// H5微信支付
if($result['trade_type'] == 'MWEB'){
if($config['sdk_qq']['gz_appid'] != $result['appid']){
return ['code' => 10005 ,'msg' => '支付类型 '.$result['trade_type'] .' 不合法'];
}
}
// 小程序支付
if($result['trade_type'] == 'JSAPI'){
if($config['mp_weixin']['appid'] != $result['appid']){
if($config['sdk_qq']['gz_appid'] != $result['appid']){
return ['code' => 10006 ,'msg' => '支付APPID不合法'];
}
}
}
// App支付
if($result['trade_type'] == 'APP'){
if($config['sdk_qq']['wx_appid'] != $result['appid']){
return ['code' => 10007 ,'msg' => '支付类型 '.$result['trade_type'] .' 不合法'];
}
}
}else{
return ['code' => 500 ,'msg' => '支付回调失败'];
}
// 回调支付
$pay_type = 1; // 支付类型:0=余额支付,1=微信支付,2=支付宝支付
$pay_name = '微信';
$trade_no = $result['transaction_id'];
}
// -----------------------------支付成功,修改订单-----------------------------
$order_list = [];
$pay_list = [];
// 拼团列表
$groups_list = [];
// 拼团列表
$groups_team_list = [];
foreach($pay as $row){
$isAlone = false; // 拼团订单一般是单订单,暂可以这样操作
$groups_state = 'start'; // 拼团状态
foreach($model_order_goods->where('order_id', $row['order_id'])->select() as $goods){
if($order_type == 'groups' && !empty($goods['group_type'])){
if($goods['group_type'] == 'alone'){
$isAlone = true;
}else{
// 查询团ID
$groups = model('app\api\model\wanlshop\groups\Groups')
->where(['group_no' => $goods['group_no']])
->find();
// 判断是否超团
$groups_team = model('app\api\model\wanlshop\groups\Team')
->where(['group_no' => $goods['group_no']])
->select();
// 已拼团总数量
$groups_team_count = count($groups_team);
if($groups_team_count >= $groups['people_num'] || $groups['join_num'] >= $groups['people_num']){
$this->error(__('参与拼单失败,拼团已完成'));
}
// 判断是否具备成团条件
if(($groups['people_num'] - $groups_team_count) <= 1 || ($groups['people_num'] - $groups['join_num']) <= 1){
$groups_state = 'success';
//调整其他拼团订单
// 订单状态:1=待支付,2=待成团,3=待发货,4=待收货,5=待评论,6=已完成,7=已取消
foreach($groups_team as $team){
$order_list[] = ['id' => $team['order_id'], 'state' => 3, 'groupstime' => time()];
}
}
// 拼团状态: ready=准备中,start=拼团中,success=已成团,fail=拼团失败,auto=自动成团
$groups_list[] = [
'id' => $groups['id'],
'join_num' => $groups['join_num'] + 1,
'state' => $groups_state
];
$groups_team_list[] = [
'user_id' => $user_id, // 1.0.9升级
'shop_id' => $goods['shop_id'],
'group_no' => $goods['group_no'],
'order_id' => $goods['order_id'],
'order_goods_id' => $goods['id']
];
}
}
// 新增付款人数、新增销量
$model_goods->where('id', $goods['goods_id'])->inc('payment')->inc('sales', $goods['number'])->update();
}
// 订单列表
if($groups_state === 'success'){
$order_list[] = ['id' => $row['order_id'], 'state' => 3, 'paymenttime' => time(), 'groupstime' => time()];
}else{
$order_list[] = ['id' => $row['order_id'], 'state' => $isAlone ? 3 : 2, 'paymenttime' => time()];
}
// 支付列表 1.0.9升级
$pay_list[] = [
'id' => $row['id'],
'trade_no' => $trade_no, // 第三方交易号
'pay_type' => $pay_type, // 支付类型:0=余额支付,1=微信支付,2=支付宝支付
'pay_state' => 1, // 支付状态 (支付回调):0=未支付,1=已支付,2=已退款
'total_amount' => $price, // 总金额
'actual_payment' => $row['price'], // 实际支付
'notice' => json_encode($result)
];
}
// 更新支付列表
model('app\api\model\wanlshop\Pay')->saveAll($pay_list);
// 更新订单列表
$model_order->saveAll($order_list);
// 支付日志
model('app\common\model\MoneyLog')->create([
'user_id' => $user_id,
'money' => -$price, // 操作金额
'memo' => $pay_name.'支付' . $order_type == 'groups' ? '拼团' : '商城' . '订单', // 备注
'type' => $order_type == 'groups' ? 'groups' : 'pay', // 类型
'service_ids' => implode(",",$order_no) // 业务ID
]);
Log::debug('Wanlpay notify', $result->all());
if($order_type == 'groups'){
model('app\api\model\wanlshop\groups\Groups')->isUpdate()->saveAll($groups_list);
model('app\api\model\wanlshop\groups\Team')->saveAll($groups_team_list);
}
// 1.1.5升级 捕获异常
} catch (\Exception $e) {
return ['code' => 10008 ,'msg' => $e->getMessage()];
}
// 返回给支付接口
return ['code' => 200 ,'msg' => $wanlpay->success()->send()];
}
/**
* 充值支付回调
*/
public function notify_recharge()
{
$wanlpay = Pay::{$this->type}(self::getConfig($this->type));
try{
$result = $wanlpay->verify();
// 查询订单是否存在
$order = model('app\api\model\wanlshop\RechargeOrder')
->where(['orderid' => $result['out_trade_no']])
->find();
if (!$order) {
return ['code' => 10001 ,'msg' => '支付订单不存在'];
}else{
if($order['status'] == 'paid'){
return ['code' => 10007 ,'msg' => '订单已经支付过'];
}
}
$memo = '';
$trade_no = '';
// -----------------------------判断订单是否合法-----------------------------
$config = get_addon_config('wanlshop');
// 支付宝
if ($this->type == 'alipay') {
// 判断状态
if (in_array($result['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
// 判断金额
if($order['amount'] != $result['total_amount']){
return ['code' => 10002 ,'msg' => '支付金额不合法'];
}
// 判断appid
if($config['sdk_alipay']['app_id'] != $result['app_id']){
return ['code' => 10003 ,'msg' => 'APPID不合法'];
}
}else{
return ['code' => 500 ,'msg' => '支付回调失败'];
}
$memo = '支付宝充值';
$trade_no = $result['trade_no'];
} else if($this->type == 'wechat'){
// 判断状态
if ($result['result_code'] == 'SUCCESS') {
// 判断金额
if($order['amount'] != ($result['total_fee'] / 100)){
return ['code' => 10002 ,'msg' => '支付金额不合法'];
}
// 判断商家ID
if($config['sdk_qq']['mch_id'] != $result['mch_id']){
return ['code' => 10004 ,'msg' => '商户不合法'];
}
// H5微信支付
if($result['trade_type'] == 'MWEB'){
if($config['sdk_qq']['gz_appid'] != $result['appid']){
return ['code' => 10005 ,'msg' => '支付类型 '.$result['trade_type'] .' 不合法'];
}
}
// 小程序支付
if($result['trade_type'] == 'JSAPI'){
if($config['mp_weixin']['appid'] != $result['appid']){
if($config['sdk_qq']['gz_appid'] != $result['appid']){
return ['code' => 10006 ,'msg' => '支付APPID不合法'];
}
}
}
// App支付
if($result['trade_type'] == 'APP'){
if($config['sdk_qq']['wx_appid'] != $result['appid']){
return ['code' => 10007 ,'msg' => '支付类型 '.$result['trade_type'] .' 不合法'];
}
}
}else{
return ['code' => 500 ,'msg' => '支付回调失败'];
}
$memo = '微信充值';
$trade_no = $result['transaction_id'];
}
// -----------------------------支付成功,修改订单-----------------------------
if ($order['status'] == 'created') {
$order->memo = $trade_no;
$order->payamount = $order['amount']; // 上面已经判断过金额,可以直接使用
$order->paytime = time();
$order->status = 'paid';
$order->save();
// 更新用户金额
self::money(+$order['amount'], $order['user_id'], $memo, 'recharge', $order['id']);
}
Log::debug('Wanlpay notify', $result->all());
// 1.1.5升级 捕获异常
} catch (\Exception $e) {
return ['code' => 10008 ,'msg' => $e->getMessage()];
}
// 返回给支付接口
return ['code' => 200 ,'msg' => $wanlpay->success()->send()];
}
/**
* 支付成功
*/
public function return()
{
$wanlpay = Pay::{$this->type}(self::getConfig($this->type));
try{
return $wanlpay->verify();
// 1.1.5升级 捕获异常
} catch (\Exception $e) {
return __($e->getMessage());
}
}
/**
* 退款回调
*/
public function notify_refund()
{
$wanlpay = Pay::{$this->type}(self::getConfig($this->type));
try{
$result = $wanlpay->verify(null, true);
// 支付宝
if ($this->type == 'wechat') {
$type_text = '微信支付';
$money = bcdiv($result['refund_fee'], 100, 2);
$pay_no = $result['out_refund_no'];
} else if($this->type == 'alipay'){
$type_text = '支付宝支付';
$money = $result['refund_amount'];
$pay_no = $result['body'];
}
// 查询支付集
// $payOutTrade = model('app\api\model\wanlshop\PayOutTrade')
// ->where('out_trade_no', 'eq', $result['out_trade_no'])
// ->find();
// 查询订单是否存在
$pay = model('app\api\model\wanlshop\Pay')
->where('pay_no', 'eq', $pay_no)
->where('pay_state', 'eq', '1')
->find();
if($pay){
$refund = model('app\api\model\wanlshop\Refund')
->where([
'user_id' => $pay['user_id'],
'order_pay_id' => $pay['id'],
'order_id' => $pay['order_id'],
'order_type' => $pay['type']
])
->find();
// 判断退款是否存在,退款状态是否第三方退款中状态
if($refund){
if($refund['state'] == '3' || $refund['state'] == '7'){
// 判断退款金额
if(bccomp($money, $refund['price'], 4) == 0){
// 更新商品状态
$this->setOrderGoodsState(3, $refund['goods_ids'], $refund['order_type']);
// 更新订单状态
$this->setRefundState($refund['order_id'], $refund['order_type']);
// 写入日志
$this->refundLog($pay['user_id'], $refund['id'], $type_text.'退款处理成功,订单金额¥'.$refund['price'].'元已到账');
// 推送开始
$this->pushRefund($refund['id'], $refund['order_id'], $refund['goods_ids'], '退款成功', $refund['order_type']);
// 更新退款
$refund->allowField(true)->save(['state' => 4,'notice' => json_encode($result),'completetime' => time()]);
}else{
// 写入日志
$this->refundLog($pay['user_id'], $refund['id'], $type_text.'退款订单异常,订单金额¥'.$refund['price'].'元,实际退款金额¥'.$money.'元');
// 推送开始
$this->pushRefund($refund['id'], $refund['order_id'], $refund['goods_ids'], $type_text.'退款金额不匹配,退款失败', $refund['order_type']);
// 更新退款
$refund->allowField(true)->save(['state' => 8,'notice' => json_encode($result),'refuse_content' => $type_text.'退款失败,金额不匹配,请联系管理员']);
}
}else{
// 写入日志
$this->refundLog($pay['user_id'], $refund['id'], $type_text.'退款订单异常');
// 推送开始
$this->pushRefund($refund['id'], $refund['order_id'], $refund['goods_ids'], $type_text.'退款订单异常,退款失败', $refund['order_type']);
// 更新退款
$refund->allowField(true)->save(['state' => 8,'notice' => json_encode($result),'refuse_content' => $type_text.'退款订单不存在,请联系管理员']);
}
}
}else{
return ['code' => 10001 ,'msg' => '支付订单不存在'];
}
} catch (\Exception $e) {
// 错误逻辑
return ['code' => 10008 ,'msg' => $e->getMessage()];
}
// 返回给支付接口
return ['code' => 200 ,'msg' => $wanlpay->success()->send()];
}
/**
* 第三方退款
* @param int $money 金额
* @param int $pay_id 会员ID
* @1.1.2升级 ~ 1.1.5升级
*/
public function refund($refund_id, $money, $pay_id)
{
$type = '';
$pay = model('app\api\model\wanlshop\Pay')->get($pay_id);
if($pay && $pay['pay_state'] == 1 && $pay['pay_type'] != 0){
// 1.1.3升级
$trade = model('app\api\model\wanlshop\PayOutTrade')
->where([['EXP', Db::raw('FIND_IN_SET('.$pay['id'].', pay_id)')]])
->find();
if(!$trade){
return ['code' => 10009 ,'msg' => '没有找到商户订单号'];
}
if($pay['pay_type'] == 1){
$type = 'wechat';
$type_text = '微信';
// 退款订单
$order['out_trade_no'] = $trade['out_trade_no'];
$order['out_refund_no'] = $pay['pay_no']; //商家订单号
// 分批退款
if(count(explode(',', $trade['pay_id'])) > 1){
// 总金额
$order['total_fee'] = bcmul(model('app\api\model\wanlshop\Pay')
->where('id', 'in', $trade['pay_id'])
->sum('price'), 100, 0);
}else{
$order['total_fee'] = bcmul($pay['price'], 100, 0); // 订单金额
}
$order['refund_fee'] = bcmul($money, 100, 0);
$order['refund_desc'] = '商城订单(订单号:'.$pay['order_no'].')退款'.$type_text.'账户¥'.$money.'元。';
$notice = json_decode($pay['notice'], true);
switch ($notice['trade_type']){
case 'JSAPI':
$order['type'] = 'miniapp';
break;
case 'APP':
$order['type'] = 'app';
break;
}
}else if($pay['pay_type'] == 2){
$type = 'alipay';
$type_text = '支付宝';
// 1.1.3升级
$order['out_trade_no'] = $trade['out_trade_no'];
$order['refund_amount'] = $money; //退款金额
$order['refund_reason'] = '商城订单(订单号:'.$pay['order_no'].')退款买家'.$type_text.'账户¥'.$money.'元。'; //退款原因说明
// 分批退款
if(count(explode(',', $trade['pay_id'])) > 1){
$order['out_request_no'] = $refund_id; //退款请求号
}
}
// 退款 1.1.5升级
try{
$payConfig = self::getConfig($type);
$payConfig['notify_url'] = str_replace("notify", "notify_refund", $payConfig['notify_url']);
$wanlpay = Pay::{$type}($payConfig)->refund($order);
$result = false;
// 注意,接口中result_code=SUCCESS,仅代表本次退款请求成功,不代表退款成功
if($type == 'wechat' && $wanlpay->result_code == 'SUCCESS'){
// 关于微信退款:
// 微信退款不提供退款结果,仅返回退款请求成功,所以必须$result=true
// 通过回调来获取结果
$result = true;
}
// 注意,接口中code=10000,仅代表本次退款请求成功,不代表退款成功
if($type == 'alipay' && $wanlpay->code == '10000'){
// fund_change = Y,代表退款成功,支付宝文档《如何判断退款是否成功》,https://opendocs.alipay.com/support/01rawa
if($wanlpay->fund_change == 'Y'){
return ['code' => 0,'msg' => 'OK','data' => [
'money' => $money,
'user_id' => $pay['user_id'],
'type_text' => $type_text,
'refund_id' => $refund_id
]];
}else{
// 通过回调来获取结果
// $result = true;
// 关于支付宝退款:
// 因测试订单时请求的均为 fund_change=Y 状态,即代表退款成功,可同步退款成功,以上注释支付宝文档《如何判断退款是否成功》
// 返回结果中的notify_url为配置的参数 也为notify_refund()方法,
// 但,实际服务器接收到异步回调为 notify()方法 且notify()中接受到的 TRADE_CLOSED交易状态为 "交易关闭"
// 目前等待支付宝进一步反馈,我们判断因为退款已成功fund_change=Y,所以返回notify() 为交易关闭
// 即使可以通过回调,可没有 同步fund_change=Y 状态安全可靠,已下
// 当前最佳最合理安全方案:
// 1.当 提交退款请求,且fund_change=Y同步完成退款(否则会出现已经给用户支付宝退款,但没有扣用户本地余额,会存在一定风险)
// 2.如果 极少数订单出现fund_change=N 时 code=10010中断第三方支付,使金额转到商城余额在手动提现,可避免任何风险产生
return ['code' => 10010 ,'msg' => '提交退款未同步退款成功'];
}
}
if($result){
return ['code' => 200,'msg' => 'OK','data' => [
'money' => $money,
'user_id' => $pay['user_id'],
'type_text' => $type_text,
'refund_id' => $refund_id
]];
}else{
return ['code' => 10009 ,'msg' => '退款失败'];
}
} catch (\Exception $e) {
return ['code' => 10008 ,'msg' => $e->getMessage()];
}
}else{
return ['code' => 1 ,'msg' => '余额实时退款'];
}
}
/**
* 变更会员余额
* @param int $money 余额
* @param int $user_id 会员ID
* @param string $memo 备注
* @param string $type 类型
* @param string $ids 业务ID
*/
public static function money($money, $user_id, $memo, $type = '', $ids = '')
{
$user = model('app\common\model\User')->get($user_id);
if ($user && $money != 0) {
$before = $user->money;
$after = function_exists('bcadd') ? bcadd($user->money, $money, 2) : $user->money + $money;
//更新会员信息
$user->save(['money' => $after]);
//写入日志
$row = model('app\common\model\MoneyLog')->create([
'user_id' => $user_id,
'money' => $money, // 操作金额
'before' => $before, // 原金额
'after' => $after, // 增加后金额
'memo' => $memo, // 备注
'type' => $type, // 类型
'service_ids' => $ids // 业务ID
]);
return $row;
}else{
return ['code' => 500 ,'msg' => '变更金额失败'];
}
}
/**
* 推送退款消息(方法内使用)
*
* @param string refund_id 订单ID
* @param string order_id 订单ID
* @param string goods_id 订单ID
* @param string title 标题
*/
private function pushRefund($refund_id = 0, $order_id = 0, $goods_id = 0, $title = '', $order_type = 'goods')
{
if($order_type === 'groups'){
$orderModel = model('app\index\model\wanlshop\groups\Order');
$orderGoodsModel = model('app\index\model\wanlshop\groups\OrderGoods');
}else{
$orderModel = model('app\index\model\wanlshop\Order');
$orderGoodsModel = model('app\index\model\wanlshop\OrderGoods');
}
$order = $orderModel->get($order_id);
$goods = $orderGoodsModel->get($goods_id);
$msg = [
'user_id' => $order['user_id'], // 推送目标用户
'shop_id' => $order['shop_id'],
'title' => $title, // 推送标题
'image' => $goods['image'], // 推送图片
'content' => '您申请退款的商品 '.(mb_strlen($goods['title'],'utf8') >= 25 ? mb_substr($goods['title'],0,25,'utf-8').'...' : $goods['title']).' '.$title,
'type' => 'order', // 推送类型
'modules' => $order_type === 'groups' ? 'groupsrefund' : 'refund', // 模块类型
'modules_id' => $refund_id, // 模块ID
'come' => '订单'.$order['order_no'] // 来自
];
$this->wanlchat->send($order['user_id'], $msg);
$notice = model('app\index\model\wanlshop\Notice');
$notice->data($msg);
$notice->allowField(true)->save();
}
/**
* 更新订单商品状态(方法内使用)
*
* @ApiSummary (WanlShop 更新订单商品状态)
* @ApiMethod (POST)
*
* @param string $status 状态
* @param string $goods_id 商品ID
*/
private function setOrderGoodsState($status = 0, $goods_id = 0, $order_type = 'goods')
{
if($order_type === 'groups'){
$orderGoodsModel = model('app\index\model\wanlshop\groups\OrderGoods');
}else{
$orderGoodsModel = model('app\index\model\wanlshop\OrderGoods');
}
return $orderGoodsModel->save(['refund_status' => $status],['id' => $goods_id]);
}
/**
* 修改订单状态(方法内使用) 1.0.5升级
*
* @ApiSummary (WanlShop 修改订单状态)
* @ApiMethod (POST)
*
* @param string $id 订单ID
*/
private function setRefundState($order_id = 0, $order_type = 'goods')
{
if($order_type === 'groups'){
$orderModel = model('app\index\model\wanlshop\groups\Order');
$orderGoodsModel = model('app\index\model\wanlshop\groups\OrderGoods');
}else{
$orderModel = model('app\index\model\wanlshop\Order');
$orderGoodsModel = model('app\index\model\wanlshop\OrderGoods');
}
$list = $orderGoodsModel
->where(['order_id' => $order_id])
->select();
$refundStatusCount = 0;
foreach($list as $row){
if($row['refund_status'] == 3) $refundStatusCount += 1;
}
// 如果订单下所有商品全部退款完毕则关闭订单
if(count($list) == $refundStatusCount){
$orderModel->save(['state' => 7],['id' => $order_id]);
return true;
}
return false;
}
/**
* 退款日志(方法内使用)
*
* @ApiSummary (WanlShop 退款日志)
* @ApiMethod (POST)
*
* @param string $user_id 用户ID
* @param string $refund_id 退款ID
* @param string $content 日志内容
* @param string $type 退款状态:0=买家,1=卖家,2=官方,3=系统
*/
private function refundLog($user_id = 0, $refund_id = 0, $content = '', $type = 3)
{
return model('app\index\model\wanlshop\RefundLog')->allowField(true)->save([
'user_id' => $user_id,
'refund_id' => $refund_id,
'type' => $type,
'content' => $content
]);
}
/**
* 获取配置 1.1.2升级
* @param string $type 支付类型
* @return array|mixed
*/
private function getConfig($type)
{
$config = get_addon_config('wanlshop');
$pay_config = [];
if($type == 'alipay'){
$pay_config = [
'app_id' => $config['sdk_alipay']['app_id'],
'notify_url' => $config['ini']['appurl'].$config['sdk_alipay']['notify_url'],
'return_url' => $config['ini']['appurl'].$config['sdk_alipay']['return_url'],
'private_key' => $config['sdk_alipay']['private_key'],
'log' => [
'file' => LOG_PATH.'wanlpay'.DS.$type.'-'.date("Y-m-d").'.log',
'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
'type' => 'single', // optional, 可选 daily.
'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
],
'http' => [
'timeout' => 5.0,
'connect_timeout' => 5.0
],
];
if (isset($config['sdk_alipay']['app_cert_public_key']) && substr($config['sdk_alipay']['app_cert_public_key'], 0, 8) == '/addons/') {
$pay_config['app_cert_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['sdk_alipay']['app_cert_public_key'], 1));
}
if (isset($config['sdk_alipay']['alipay_root_cert']) && substr($config['sdk_alipay']['alipay_root_cert'], 0, 8) == '/addons/') {
$pay_config['alipay_root_cert'] = ROOT_PATH . str_replace('/', DS, substr($config['sdk_alipay']['alipay_root_cert'], 1));
}
if (isset($config['sdk_alipay']['ali_public_key']) && (Str::endsWith($config['sdk_alipay']['ali_public_key'], '.crt') || Str::endsWith($config['sdk_alipay']['ali_public_key'], '.pem'))) {
$pay_config['ali_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['sdk_alipay']['ali_public_key'], 1));
}
}else if($type == 'wechat'){
$pay_config = [
'appid' => $config['sdk_qq']['wx_appid'], // APP APPID
'app_id' => $config['sdk_qq']['gz_appid'], // 公众号 APPID
'miniapp_id' => $config['mp_weixin']['appid'], // 小程序 APPID
'mch_id' => $config['sdk_qq']['mch_id'],
'key' => $config['sdk_qq']['key'],
'notify_url' => $config['ini']['appurl']. $config['sdk_qq']['notify_url'],
// 1.0.8升级 回调 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
'return_url' => $config['h5']['domain'].($config['h5']['router_mode'] == 'hash' ? '/#':'') . '/pages/page/success?type=pay',
'log' => [
'file' => LOG_PATH.'wanlpay'.DS.$type.'-'.date("Y-m-d").'.log',
'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
'type' => 'single', // optional, 可选 daily.
'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
],
'http' => [
'timeout' => 5.0,
'connect_timeout' => 5.0,
// 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
],
];
if (isset($config['sdk_qq']['cert_client']) && substr($config['sdk_qq']['cert_client'], 0, 8) == '/addons/') {
$pay_config['cert_client'] = ROOT_PATH . str_replace('/', DS, substr($config['sdk_qq']['cert_client'], 1));
}
if (isset($config['sdk_qq']['cert_key']) && substr($config['sdk_qq']['cert_key'], 0, 8) == '/addons/') {
$pay_config['cert_key'] = ROOT_PATH . str_replace('/', DS, substr($config['sdk_qq']['cert_key'], 1));
}
}
return $pay_config;
}
}