比较提交
2 次代码提交
0ee8f55aba
...
54e2d6eec9
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
54e2d6eec9 | ||
|
|
a2c24aff15 |
451
addons/shopro/controller/Pay.php
普通文件
451
addons/shopro/controller/Pay.php
普通文件
@@ -0,0 +1,451 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\controller;
|
||||
|
||||
use addons\epay\library\Service;
|
||||
use fast\Random;
|
||||
use think\addons\Controller;
|
||||
use addons\shopro\exception\Exception;
|
||||
use addons\shopro\model\Order;
|
||||
use addons\shopro\model\User;
|
||||
use addons\shopro\model\TradeOrder;
|
||||
use think\Db;
|
||||
use think\Log;
|
||||
|
||||
class Pay extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['prepay', 'notifyx', 'notifyr', 'alipay'];
|
||||
protected $noNeedRight = ['*'];
|
||||
|
||||
/**
|
||||
* 支付宝网页支付
|
||||
*/
|
||||
public function alipay()
|
||||
{
|
||||
$order_sn = $this->request->get('order_sn');
|
||||
|
||||
list($order, $prepay_type) = $this->getOrderInstance($order_sn);
|
||||
$order = $order->where('order_sn', $order_sn)->find();
|
||||
|
||||
try {
|
||||
if (!$order) {
|
||||
throw new \Exception("订单不存在");
|
||||
}
|
||||
if ($order->status > 0) {
|
||||
throw new \Exception("订单已支付");
|
||||
}
|
||||
if ($order->status < 0) {
|
||||
throw new \Exception("订单已失效");
|
||||
}
|
||||
|
||||
$notify_url = $this->request->root(true) . '/addons/shopro/pay/notifyx/payment/alipay/platform/H5';
|
||||
$pay = new \addons\shopro\library\PayService('alipay', 'url', $notify_url);
|
||||
|
||||
$order_data = [
|
||||
'order_id' => $order->id,
|
||||
'out_trade_no' => $order->order_sn,
|
||||
'total_fee' => $order->total_fee,
|
||||
'subject' => '商城订单支付',
|
||||
];
|
||||
|
||||
$result = $pay->create($order_data);
|
||||
|
||||
$result = $result->getContent();
|
||||
|
||||
echo $result;
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
// $this->assign('result', $result);
|
||||
|
||||
// return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉起支付
|
||||
*/
|
||||
public function prepay()
|
||||
{
|
||||
checkEnv('yansongda');
|
||||
|
||||
$order_sn = $this->request->post('order_sn');
|
||||
$orderId = $this->request->post('order_id');
|
||||
$payment = $this->request->post('payment');
|
||||
$openid = $this->request->post('openid', '');
|
||||
$user = $this->auth->getUser();
|
||||
$platform = request()->header('platform');
|
||||
// 重新获取订单号
|
||||
if (strpos($order_sn, 'TO') !== 0) {
|
||||
$order_sn = Db::name('shopro_order')
|
||||
->where(['id' => $orderId])
|
||||
->value('order_sn');
|
||||
}
|
||||
|
||||
list($order, $prepay_type) = $this->getOrderInstance($order_sn);
|
||||
$order = $order->nopay()->where('order_sn', $order_sn)->find();
|
||||
|
||||
if (!$order) {
|
||||
$this->error("订单不存在");
|
||||
}
|
||||
|
||||
if (!$payment || !in_array($payment, ['wechat', 'alipay', 'wallet', 'group'])) {
|
||||
$this->error("支付类型不能为空");
|
||||
}
|
||||
|
||||
if ($payment == 'wallet' && $prepay_type == 'order') {
|
||||
// 余额支付
|
||||
$this->walletPay($order, $payment, $platform);
|
||||
}
|
||||
|
||||
$order_data = [
|
||||
'order_id' => $order->id,
|
||||
'out_trade_no' => $order->order_sn,
|
||||
'total_fee' => $order->total_fee,
|
||||
];
|
||||
|
||||
if (strpos($order_sn, 'TO') !== 0) {
|
||||
// 保持 order_sn 不变,避免支付回调时找不到订单
|
||||
if ($payment == 'group') {
|
||||
$payment = 'wechat';
|
||||
// 重新计算价格
|
||||
$order_data['total_fee'] = bcsub($order_data['total_fee'], $user->money, 2);
|
||||
// 更新组合付款余额
|
||||
Db::name('shopro_order')->update([
|
||||
'id' => $order->id,
|
||||
'pay_type' => 'group',
|
||||
'pay_money' => $user->money
|
||||
]);
|
||||
} else {
|
||||
Db::name('shopro_order')->update([
|
||||
'id' => $order->id,
|
||||
'pay_type' => $payment,
|
||||
'pay_money' => 0
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 微信公众号,小程序支付,必须有 openid
|
||||
if ($payment == 'wechat') {
|
||||
if (in_array($platform, ['wxOfficialAccount', 'wxMiniProgram'])) {
|
||||
if (isset($openid) && $openid) {
|
||||
// 如果传的有 openid
|
||||
$order_data['openid'] = $openid;
|
||||
} else {
|
||||
// 没有 openid 默认拿下单人的 openid
|
||||
$oauth = \addons\shopro\model\UserOauth::where([
|
||||
'user_id' => $order->user_id,
|
||||
'provider' => 'Wechat',
|
||||
'platform' => $platform
|
||||
])->find();
|
||||
|
||||
$order_data['openid'] = $oauth ? $oauth->openid : '';
|
||||
}
|
||||
|
||||
if (empty($order_data['openid'])) {
|
||||
// 缺少 openid
|
||||
return $this->success('缺少 openid', 'no_openid');
|
||||
}
|
||||
}
|
||||
|
||||
$order_data['body'] = '商城订单支付';
|
||||
} else {
|
||||
$order_data['subject'] = '商城订单支付';
|
||||
}
|
||||
|
||||
$notify_url = $this->request->root(true) . '/addons/shopro/pay/notifyx/payment/' . $payment . '/platform/' . $platform;
|
||||
$pay = new \addons\shopro\library\PayService($payment, $platform, $notify_url);
|
||||
|
||||
try {
|
||||
$result = $pay->create($order_data);
|
||||
} catch (\Yansongda\Pay\Exceptions\Exception $e) {
|
||||
$this->error("支付配置错误:" . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($platform == 'App') {
|
||||
// $result = json_decode($result->getContent(), true);
|
||||
$result = $result->getContent();
|
||||
}
|
||||
if ($platform == 'H5' && $payment == 'wechat') {
|
||||
$result = $result->getContent();
|
||||
}
|
||||
|
||||
return $this->success('获取预付款成功', [
|
||||
'pay_data' => $result,
|
||||
'pay_action' => $pay->method,
|
||||
]);
|
||||
}
|
||||
|
||||
// 余额支付
|
||||
public function walletPay($order, $type, $method)
|
||||
{
|
||||
$order = Db::transaction(function () use ($order, $type, $method) {
|
||||
// 重新加锁读,防止连点问题
|
||||
$order = Order::nopay()->where('order_sn', $order->order_sn)->lock(true)->find();
|
||||
if (!$order) {
|
||||
$this->error("订单已支付");
|
||||
}
|
||||
$total_fee = $order->total_fee;
|
||||
|
||||
// 扣除余额
|
||||
$user = User::info();
|
||||
|
||||
if (is_null($user)) {
|
||||
// 没有登录,请登录
|
||||
$this->error(__('Please login first'), null, 401);
|
||||
}
|
||||
|
||||
User::money(-$total_fee, $user->id, 'wallet_pay', $order->id, '', [
|
||||
'order_id' => $order->id,
|
||||
'order_sn' => $order->order_sn,
|
||||
]);
|
||||
|
||||
// 支付后流程
|
||||
$notify = [
|
||||
'order_sn' => $order['order_sn'],
|
||||
'transaction_id' => '',
|
||||
'notify_time' => date('Y-m-d H:i:s'),
|
||||
'buyer_email' => $user->id,
|
||||
'pay_fee' => $order->total_fee,
|
||||
'pay_type' => 'wallet' // 支付方式
|
||||
];
|
||||
$notify['payment_json'] = json_encode($notify);
|
||||
$order->paymentProcess($order, $notify);
|
||||
|
||||
return $order;
|
||||
});
|
||||
|
||||
$this->success('支付成功', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付成功回调
|
||||
*/
|
||||
public function notifyx()
|
||||
{
|
||||
Log::write('notifyx-comein:');
|
||||
|
||||
$payment = $this->request->param('payment');
|
||||
$platform = $this->request->param('platform');
|
||||
|
||||
$pay = new \addons\shopro\library\PayService($payment, $platform);
|
||||
|
||||
$result = $pay->notify(function ($data, $pay) use ($payment, $platform) {
|
||||
Log::write('notifyx-result:' . json_encode($data));
|
||||
// { // 微信回调参数
|
||||
// "appid":"wx39cd0799d4567dd0",
|
||||
// "bank_type":"OTHERS",
|
||||
// "cash_fee":"1",
|
||||
// "fee_type":"CNY",
|
||||
// "is_subscribe":"N",
|
||||
// "mch_id":"1481069012",
|
||||
// "nonce_str":"dPpcZ6AzCDU8daNC",
|
||||
// "openid":"oD9ko4x7QMDQPZEuN8V5jtZjie3g",
|
||||
// "out_trade_no":"202010240834057843002700",
|
||||
// "result_code":"SUCCESS",
|
||||
// "return_code":"SUCCESS",
|
||||
// "sign":"3103B6D06F13D2B3959C5B3F7F1FD61C",
|
||||
// "time_end":"20200407102424",
|
||||
// "total_fee":"1",
|
||||
// "trade_type":"JSAPI",
|
||||
// "transaction_id":"4200000479202004070804485027"
|
||||
// }
|
||||
|
||||
// { // 支付宝支付成功回调参数
|
||||
// "gmt_create":"2020-04-10 19:15:00",
|
||||
// "charset":"utf-8",
|
||||
// "seller_email":"xptech@qq.com",
|
||||
// "subject":"\u5546\u57ce\u8ba2\u5355\u652f\u4ed8",
|
||||
// "sign":"AOiYZ1a2mEMOuIbHFCi6V6A0LJ97UMiHsCWgNdSU9dlzKFl15Ts8b0mL\/tN+Hhskl+94S3OUiNTBD3dD0Kv923SqaTWxNdj533PCdo2GDKsZIZgKbavnOvaccSKUdmQRE9KtmePPq9V9lFzEQvdUkKq1M8KAWO5K9LTy2iT2y5CUynpiu\/04GVzsTL9PqY+LDwqj6K+w7MgceWm1BWaFWg27AXIRw7wvsFckr3k9GGajgE2fufhoCYGYtGFbhGOp6ExtqS5RXBuPODOyRhBLpD8mwpOX38Oy0X+R4YQIrOi02dhqwPpvw79YjnvgXY3qZEQ66EdUsrT7EBdcPHK0Gw==",
|
||||
// "buyer_id":"2088902485164146",
|
||||
// "invoice_amount":"0.01",
|
||||
// "notify_id":"2020041000222191501064141414240102",
|
||||
// "fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"PCREDIT\"}]",
|
||||
// "notify_type":"trade_status_sync",
|
||||
// "trade_status":"TRADE_SUCCESS",
|
||||
// "receipt_amount":"0.01",
|
||||
// "buyer_pay_amount":"0.01",
|
||||
// "app_id":"2021001114666742",
|
||||
// "sign_type":"RSA2",
|
||||
// "seller_id":"2088721922277739",
|
||||
// "gmt_payment":"2020-04-10 19:15:00",
|
||||
// "notify_time":"2020-04-10 19:15:01",
|
||||
// "version":"1.0",
|
||||
// "out_trade_no":"202007144778322770017000",
|
||||
// "total_amount":"0.01",
|
||||
// "trade_no":"2020041022001464141443020240",
|
||||
// "auth_app_id":"2021001114666742",
|
||||
// "buyer_logon_id":"157***@163.com",
|
||||
// "point_amount":"0.00"
|
||||
// }
|
||||
|
||||
// { // 支付宝退款成功(交易关闭)回调参数
|
||||
// "gmt_create": "2020-08-15 14:48:32",
|
||||
// "charset": "utf-8",
|
||||
// "seller_email": "xptech@qq.com",
|
||||
// "gmt_payment": "2020-08-15 14:48:32",
|
||||
// "notify_time": "2020-08-15 16:11:45",
|
||||
// "subject": "商城订单支付",
|
||||
// "gmt_refund": "2020-08-15 16:11:45.140",
|
||||
// "sign": "b6ArkgzLIRteRL9FMGC6i\/jf6VwFYQbaGDGRx002W+pdmN5q9+O4edZ3ALF74fYaijSl9ksNr0dKdvanu3uYxBTcd\/GIS4N1CWzmCOv6pzgx5rO\/YvGoHLM3Yop0GKKuMxmnNsZ6jhYKEY7SYD3Y0L6PU9ZMdHV7yIiVj+zZmbKzUgK9MPDCEXs+nzpNAiSM8GTqYRSUvKobAK68hswG2k1QIcqr5z+ZmVYa\/nHHkoC9qXt5zwyGi4P+2eOsr6V2PjA3x8qqe7TN5aI1DeoZD5KqHPYYaYF17J2q6YPlgl3WUl1RhE7H86bivB1fIuYEv\/3+JR74WN\/o7krGw1RPHg==",
|
||||
// "out_biz_no": "R202004114414846255015300",
|
||||
// "buyer_id": "2088902485164146",
|
||||
// "version": "1.0",
|
||||
// "notify_id": "2020081500222161145064141453349793",
|
||||
// "notify_type": "trade_status_sync",
|
||||
// "out_trade_no": "202002460317545607015300",
|
||||
// "total_amount": "0.01",
|
||||
// "trade_status": "TRADE_CLOSED",
|
||||
// "refund_fee": "0.01",
|
||||
// "trade_no": "2020081522001464141438570535",
|
||||
// "auth_app_id": "2021001114666742",
|
||||
// "buyer_logon_id": "157***@163.com",
|
||||
// "gmt_close": "2020-08-15 16:11:45",
|
||||
// "app_id": "2021001114666742",
|
||||
// "sign_type": "RSA2",
|
||||
// "seller_id": "2088721922277739"
|
||||
// }
|
||||
|
||||
try {
|
||||
$out_trade_no = $data['out_trade_no'];
|
||||
$out_refund_no = $data['out_biz_no'] ?? '';
|
||||
|
||||
list($order, $prepay_type) = $this->getOrderInstance($out_trade_no);
|
||||
|
||||
// 判断是否是支付宝退款(支付宝退款成功会通知该接口)
|
||||
if ($payment == 'alipay' // 支付宝支付
|
||||
&& $data['notify_type'] == 'trade_status_sync' // 同步交易状态
|
||||
&& $data['trade_status'] == 'TRADE_CLOSED' // 交易关闭
|
||||
&& $out_refund_no // 退款单号
|
||||
) {
|
||||
// 退款回调
|
||||
if ($prepay_type == 'order') {
|
||||
// 退款回调
|
||||
$this->refundFinish($out_trade_no, $out_refund_no);
|
||||
} else {
|
||||
// 其他订单如果支持退款,逻辑这里补充
|
||||
}
|
||||
|
||||
return $pay->success()->send();
|
||||
}
|
||||
|
||||
// 判断支付宝微信是否是支付成功状态,如果不是,直接返回响应
|
||||
if ($payment == 'alipay' && $data['trade_status'] != 'TRADE_SUCCESS') {
|
||||
// 不是交易成功的通知,直接返回成功
|
||||
return $pay->success()->send();
|
||||
}
|
||||
if ($payment == 'wechat' && ($data['result_code'] != 'SUCCESS' || $data['return_code'] != 'SUCCESS')) {
|
||||
// 微信交易未成功,返回 false,让微信再次通知
|
||||
return false;
|
||||
}
|
||||
|
||||
// 支付成功流程
|
||||
$pay_fee = $payment == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
|
||||
|
||||
// 你可以在此编写订单逻辑
|
||||
$order = $order->nopay()->where('order_sn', $out_trade_no)->lock(true)->find();
|
||||
|
||||
if (!$order) {
|
||||
// 已支付则直接返回成功,否则记录日志并通知微信重试
|
||||
$paidOrder = Order::where('order_sn', $out_trade_no)->where('status', '>', 0)->find();
|
||||
if ($paidOrder) {
|
||||
return $pay->success()->send();
|
||||
}
|
||||
Log::write('notifyx-order-not-found:' . $out_trade_no);
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::transaction(function () use ($order, $data, $payment, $platform, $pay_fee, $prepay_type) {
|
||||
$notify = [
|
||||
'order_sn' => $data['out_trade_no'],
|
||||
'transaction_id' => $payment == 'alipay' ? $data['trade_no'] : $data['transaction_id'],
|
||||
'notify_time' => date('Y-m-d H:i:s', strtotime($data['time_end'])),
|
||||
'buyer_email' => $payment == 'alipay' ? $data['buyer_logon_id'] : $data['openid'],
|
||||
'payment_json' => json_encode($data->all()),
|
||||
'pay_fee' => $pay_fee,
|
||||
'pay_type' => $order->pay_type == 'group' ? 'group' : $payment // 支付方式
|
||||
];
|
||||
|
||||
$order->paymentProcess($order, $notify);
|
||||
});
|
||||
|
||||
return $pay->success()->send();
|
||||
} catch (\Exception $e) {
|
||||
Log::write('notifyx-error:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款成功回调
|
||||
*/
|
||||
public function notifyr()
|
||||
{
|
||||
Log::write('notifyreturn-comein:');
|
||||
|
||||
$payment = $this->request->param('payment');
|
||||
$platform = $this->request->param('platform');
|
||||
|
||||
$pay = new \addons\shopro\library\PayService($payment, $platform);
|
||||
|
||||
$result = $pay->notifyRefund(function ($data, $pay) use ($payment, $platform) {
|
||||
Log::write('notifyr-result:' . json_encode($data));
|
||||
try {
|
||||
$out_refund_no = $data['out_refund_no'];
|
||||
$out_trade_no = $data['out_trade_no'];
|
||||
|
||||
// 退款
|
||||
$this->refundFinish($out_trade_no, $out_refund_no);
|
||||
|
||||
return $pay->success()->send();
|
||||
} catch (\Exception $e) {
|
||||
Log::write('notifyreturn-error:' . json_encode($e->getMessage()));
|
||||
}
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function refundFinish($out_trade_no, $out_refund_no)
|
||||
{
|
||||
$order = Order::where('order_sn', $out_trade_no)->find();
|
||||
$refundLog = \app\admin\model\shopro\order\RefundLog::where('refund_sn', $out_refund_no)->find();
|
||||
|
||||
if (!$order || !$refundLog || $refundLog->status != 0) {
|
||||
// 订单不存在,或者订单已退款
|
||||
return true;
|
||||
}
|
||||
|
||||
$item = \app\admin\model\shopro\order\OrderItem::where('id', $refundLog->order_item_id)->find();
|
||||
|
||||
Db::transaction(function () use ($order, $item, $refundLog) {
|
||||
\app\admin\model\shopro\order\Order::refundFinish($order, $item, $refundLog);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号获取订单实例
|
||||
*
|
||||
* @param [type] $order_sn
|
||||
* @return void
|
||||
*/
|
||||
private function getOrderInstance($order_sn)
|
||||
{
|
||||
$prepay_type = 'order';
|
||||
if (strpos($order_sn, 'TO') === 0) {
|
||||
// 充值订单
|
||||
$prepay_type = 'recharge';
|
||||
$order = new TradeOrder();
|
||||
} else {
|
||||
// 订单
|
||||
$order = new Order();
|
||||
}
|
||||
|
||||
return [$order, $prepay_type];
|
||||
}
|
||||
}
|
||||
273
addons/shopro/library/PayService.php
普通文件
273
addons/shopro/library/PayService.php
普通文件
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library;
|
||||
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Log;
|
||||
use addons\shopro\exception\Exception;
|
||||
|
||||
class PayService
|
||||
{
|
||||
protected $config;
|
||||
protected $platform;
|
||||
protected $payment;
|
||||
protected $notify_url;
|
||||
public $method;
|
||||
|
||||
public function __construct($payment, $platform = '', $notify_url = '', $type = 'pay')
|
||||
{
|
||||
$this->platform = $platform;
|
||||
$this->payment = $payment;
|
||||
$this->notify_url = $notify_url;
|
||||
$this->type = $type;
|
||||
|
||||
$this->setPaymentConfig();
|
||||
}
|
||||
|
||||
private function setPaymentConfig()
|
||||
{
|
||||
if ($this->platform === 'App' && $this->payment != 'alipay') {
|
||||
$paymentConfig = json_decode(\addons\shopro\model\Config::get(['name' => 'wechat_app'])->value, true);
|
||||
} else {
|
||||
$paymentConfig = json_decode(\addons\shopro\model\Config::get(['name' => $this->payment])->value, true);
|
||||
}
|
||||
|
||||
// 如果是支付,并且不是 复制地址的支付宝支付
|
||||
if ($this->type == 'pay' && $this->platform != 'url' && !in_array($this->platform, $paymentConfig['platform'])) {
|
||||
new Exception('暂不支持该方式付款');
|
||||
}
|
||||
|
||||
$this->config = $paymentConfig;
|
||||
$this->config['notify_url'] = $this->notify_url;
|
||||
|
||||
if ($this->payment === 'wechat') {
|
||||
// 根据不同平台设置相应的 appid
|
||||
$this->setWechatAppId();
|
||||
}
|
||||
|
||||
// 设置支付证书路径
|
||||
$this->setCert();
|
||||
}
|
||||
|
||||
private function setWechatAppId()
|
||||
{
|
||||
switch ($this->platform) {
|
||||
case 'wxOfficialAccount':
|
||||
$platformConfig = json_decode(\addons\shopro\model\Config::get(['name' => $this->platform])->value, true);
|
||||
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
|
||||
$this->config['sub_app_id'] = $platformConfig['app_id'];
|
||||
} else {
|
||||
$this->config['app_id'] = $platformConfig['app_id'];
|
||||
}
|
||||
break;
|
||||
case 'wxMiniProgram':
|
||||
$platformConfig = json_decode(\addons\shopro\model\Config::get(['name' => $this->platform])->value, true);
|
||||
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
|
||||
$this->config['sub_miniapp_id'] = $platformConfig['app_id'];
|
||||
// $this->config['sub_app_id'] = $platformConfig['app_id'];
|
||||
} else {
|
||||
$this->config['miniapp_id'] = $platformConfig['app_id'];
|
||||
$this->config['app_id'] = $platformConfig['app_id']; // 小程序微信企业付款
|
||||
}
|
||||
break;
|
||||
case 'H5':
|
||||
$platformConfig = json_decode(\addons\shopro\model\Config::get(['name' => $this->platform])->value, true);
|
||||
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
|
||||
$this->config['sub_app_id'] = $platformConfig['app_id'];
|
||||
} else {
|
||||
$this->config['app_id'] = $platformConfig['app_id'];
|
||||
}
|
||||
break;
|
||||
case 'App':
|
||||
$platformConfig = json_decode(\addons\shopro\model\Config::get(['name' => 'App'])->value, true);
|
||||
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
|
||||
$this->config['sub_appid'] = $platformConfig['app_id'];
|
||||
$this->config['sub_app_id'] = $platformConfig['app_id'];
|
||||
} else {
|
||||
$this->config['appid'] = $platformConfig['app_id']; // 微信 App 支付使用这个
|
||||
$this->config['app_id'] = $platformConfig['app_id']; // 微信 App 支付退款使用的是这个
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理证书路径
|
||||
private function setCert()
|
||||
{
|
||||
// 处理证书路径
|
||||
if ($this->payment == 'wechat') {
|
||||
// 微信支付证书
|
||||
$this->config['cert_client'] = ROOT_PATH . 'public' . $this->config['cert_client'];
|
||||
$this->config['cert_key'] = ROOT_PATH . 'public' . $this->config['cert_key'];
|
||||
} else {
|
||||
// 支付宝证书路径
|
||||
$end = substr($this->config['ali_public_key'], -4);
|
||||
if ($end == '.crt') {
|
||||
$this->config['ali_public_key'] = ROOT_PATH . 'public' . $this->config['ali_public_key'];
|
||||
}
|
||||
$this->config['app_cert_public_key'] = ROOT_PATH . 'public' . $this->config['app_cert_public_key'];
|
||||
$this->config['alipay_root_cert'] = ROOT_PATH . 'public' . $this->config['alipay_root_cert'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function setPaymentMethod()
|
||||
{
|
||||
$method = [
|
||||
'wechat' => [
|
||||
'wxOfficialAccount' => 'mp', //公众号支付 Collection
|
||||
'wxMiniProgram' => 'miniapp', //小程序支付
|
||||
'H5' => 'wap', //手机网站支付 Response
|
||||
'App' => 'app' //APP 支付 JsonResponse
|
||||
],
|
||||
'alipay' => [
|
||||
'wxOfficialAccount' => 'wap', //手机网站支付 Response
|
||||
'wxMiniProgram' => 'wap', //小程序支付
|
||||
'H5' => 'wap', //手机网站支付 Response
|
||||
'url' => 'wap', //手机网站支付 Response
|
||||
'App' => 'app' //APP 支付 JsonResponse
|
||||
],
|
||||
];
|
||||
|
||||
$this->method = $method[$this->payment][$this->platform];
|
||||
}
|
||||
|
||||
public function create($order)
|
||||
{
|
||||
// $order = [
|
||||
// 'out_trade_no' => time(),
|
||||
// 'total_fee' => '1', // **单位:分**
|
||||
// 'body' => 'test body - 测试',
|
||||
// 'openid' => 'onkVf1FjWS5SBIixxxxxxx', //微信需要带openid过来
|
||||
// ];
|
||||
// 设置支付方式
|
||||
$this->setPaymentMethod();
|
||||
|
||||
$method = $this->method;
|
||||
|
||||
switch ($this->payment) {
|
||||
case 'wechat':
|
||||
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
|
||||
$order['sub_openid'] = $order['openid'];
|
||||
unset($order['openid']);
|
||||
}
|
||||
|
||||
$order['total_fee'] = $order['total_fee'] * 100;
|
||||
$pay = Pay::wechat($this->config)->$method($order);
|
||||
break;
|
||||
case 'alipay':
|
||||
if (in_array($this->platform, ['wxOfficialAccount', 'wxMiniProgram', 'H5'])) {
|
||||
// 返回支付宝支付链接
|
||||
$pay = request()->domain() . '/addons/shopro/pay/alipay?order_sn=' . $order['out_trade_no'];
|
||||
} else {
|
||||
if ($this->method == 'wap') {
|
||||
// 支付宝 wap 支付,增加 return_url
|
||||
// 获取 h5 域名
|
||||
$platformConfig = json_decode(\addons\shopro\model\Config::get(['name' => 'shopro'])->value, true);
|
||||
// 如果域名存在,增加 return_url
|
||||
if ($platformConfig && isset($platformConfig['domain'])) {
|
||||
$start = substr($platformConfig['domain'], -1) == '/' ? "" : "/";
|
||||
$orderType = strpos($order['out_trade_no'], 'TO') === 0 ? 'recharge' : 'goods';
|
||||
$this->config['return_url'] = $platformConfig['domain'] . $start . "pages/order/payment/result?orderId=" . $order['order_id'] . "&type=alipay&payState=success&orderType=" . $orderType;
|
||||
}
|
||||
}
|
||||
|
||||
$pay = Pay::alipay($this->config)->$method($order);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return $pay;
|
||||
}
|
||||
|
||||
// 企业付款
|
||||
public function transfer($payload)
|
||||
{
|
||||
$code = 0;
|
||||
$response = [];
|
||||
switch ($this->payment) {
|
||||
case 'wechat':
|
||||
$payload['amount'] = $payload['amount'] * 100;
|
||||
$response = Pay::wechat($this->config)->transfer($payload);
|
||||
if ($response['return_code'] === 'SUCCESS' && $response['result_code'] === 'SUCCESS') {
|
||||
$code = 1;
|
||||
}
|
||||
break;
|
||||
case 'alipay':
|
||||
$response = Pay::alipay($this->config)->transfer($payload);
|
||||
if ($response['code'] === '10000' && $response['status'] === 'SUCCESS') {
|
||||
$code = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return [$code, $response];
|
||||
}
|
||||
|
||||
|
||||
public function notify($callback)
|
||||
{
|
||||
$pay = $this->getPay();
|
||||
|
||||
try {
|
||||
$data = $pay->verify(); // 是的,验签就这么简单!
|
||||
|
||||
$result = $callback($data, $pay);
|
||||
|
||||
// Log::debug('Wechat notify', $data->all());
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::error('notify-error:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function refund($order_data)
|
||||
{
|
||||
$pay = $this->getPay();
|
||||
|
||||
$order_data['type'] = $this->platform == 'wxMiniProgram' ? 'miniapp' : '';
|
||||
|
||||
$result = $pay->refund($order_data);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function notifyRefund($callback)
|
||||
{
|
||||
$pay = $this->getPay();
|
||||
|
||||
try {
|
||||
$data = $pay->verify(null, true); // 是的,验签就这么简单!
|
||||
|
||||
$result = $callback($data, $pay);
|
||||
|
||||
// Log::debug('Wechat notify', $data->all());
|
||||
} catch (\Exception $e) {
|
||||
// $e->getMessage();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
private function getPay()
|
||||
{
|
||||
switch ($this->payment) {
|
||||
case 'wechat':
|
||||
$pay = Pay::wechat($this->config);
|
||||
break;
|
||||
case 'alipay':
|
||||
$pay = Pay::alipay($this->config);
|
||||
break;
|
||||
default:
|
||||
new Exception('支付方式不支持');
|
||||
}
|
||||
|
||||
return $pay;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,977 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\traits\model\order;
|
||||
|
||||
use addons\shopro\exception\Exception;
|
||||
use addons\shopro\model\Coupons;
|
||||
use addons\shopro\model\RobOrderModel;
|
||||
use addons\shopro\model\User;
|
||||
use addons\shopro\model\GoodsComment;
|
||||
use addons\shopro\model\Order;
|
||||
use addons\shopro\model\OrderAction;
|
||||
use addons\shopro\model\OrderItem;
|
||||
use addons\shopro\model\Store;
|
||||
use addons\shopro\model\UserAddress;
|
||||
use addons\shopro\model\Verify;
|
||||
use app\admin\model\shopro\order\RefundLog;
|
||||
use function fast\e;
|
||||
use think\Db;
|
||||
|
||||
trait OrderOper
|
||||
{
|
||||
use OrderOperSendGet, OrderOperCreate;
|
||||
|
||||
// 获取订单号
|
||||
public static function getSn($user_id)
|
||||
{
|
||||
$rand = $user_id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
|
||||
$order_sn = date('Yhis') . $rand;
|
||||
|
||||
$id = str_pad($user_id, (24 - strlen($order_sn)), '0', STR_PAD_BOTH);
|
||||
|
||||
return $order_sn . $id;
|
||||
}
|
||||
|
||||
// 计算订单
|
||||
public static function pre($params, $calc_type = 'pre')
|
||||
{
|
||||
self::$calc_type = $calc_type;
|
||||
$user = User::info();
|
||||
|
||||
// 检测必要系统环境
|
||||
checkEnv(['bcmath', 'queue']);
|
||||
|
||||
// 获取请求参数 "order_type", "groupon_id", "buy_type"
|
||||
extract(self::preParams($params));
|
||||
|
||||
// 检测并重新组装要购买的商品列表
|
||||
list(
|
||||
$new_goods_list, // 组合好的新的商品列表
|
||||
$activity_type, // 订单参与的活动类型,后面还需计算一次
|
||||
$activity_discounts, // 订单可能参与的所有活动
|
||||
$need_address, // 是否需要收货地址
|
||||
$user_address // 用户收货地址
|
||||
) = self::preCheck($params);
|
||||
|
||||
// 计算订单商品价格,所需积分,运费
|
||||
list(
|
||||
$new_goods_list, // 新的商品列表
|
||||
$goods_original_amount, // 商品原始总价
|
||||
$goods_amount, // 商品现在总价
|
||||
$dispatch_amount, // 订单运费(所有商品种最高的,后面还需根据活动再次计算)
|
||||
$score_amount, // 订单所需支付积分
|
||||
$deliveryFee,
|
||||
$dispatchData,
|
||||
) = self::preCalcAmount($params, $new_goods_list, $user_address, $dispatch_id);
|
||||
|
||||
// 记录原始运费
|
||||
$origin_dispatch_amount = $dispatch_amount;
|
||||
|
||||
// 计算订单优惠折扣,优惠券,邮费
|
||||
list(
|
||||
$new_goods_list, // 重新赋值活动, 商品上增加了 activity_type
|
||||
$activity_discount_infos, // 每个活动包含的优惠信息
|
||||
$activity_discount_money, // 促销活动优惠总金额
|
||||
$dispatch_discount_money, // 邮费总优惠金额
|
||||
$free_shipping_goods_ids, // 包邮的商品的ids
|
||||
$activity_type, // 全部参与的活动类型
|
||||
$dispatch_amount, // 重新计算的运费
|
||||
$user_coupons, // 使用的优惠券信息
|
||||
$coupon_money // 优惠券优惠金额
|
||||
) = self::preCalcDiscount(
|
||||
$params,
|
||||
$new_goods_list,
|
||||
$activity_discounts,
|
||||
$activity_type,
|
||||
$goods_amount,
|
||||
$dispatch_amount,
|
||||
$user_address
|
||||
);
|
||||
|
||||
// 计算订单总金额,需支付金额
|
||||
list(
|
||||
$new_goods_list,
|
||||
$total_amount,
|
||||
$discount_fee,
|
||||
$total_fee,
|
||||
$coupon_fee,
|
||||
) = self::preCalcOrder(
|
||||
$new_goods_list,
|
||||
$goods_amount,
|
||||
$origin_dispatch_amount,
|
||||
$dispatch_amount,
|
||||
$activity_discount_infos,
|
||||
$activity_discount_money,
|
||||
$dispatch_discount_money,
|
||||
$free_shipping_goods_ids,
|
||||
$coupon_money,
|
||||
$deliveryFee
|
||||
);
|
||||
|
||||
// 获取发票金额
|
||||
$invoice_amount = self::preCalcInvoiceAmount($total_fee, $goods_amount);
|
||||
|
||||
// 获取快递列表(即将废弃)
|
||||
$expressName = '';
|
||||
$expressList = Db::name('shopro_dispatch')
|
||||
->where(['type' => 'express'])
|
||||
->field(['id', 'name', 'type_ids'])
|
||||
->select();
|
||||
if (!empty($expressList) && !empty($dispatchData)) {
|
||||
foreach ($expressList as &$v) {
|
||||
if ($v['id'] === $dispatchData['dispatch_id']) {
|
||||
$v['active'] = 1;
|
||||
$expressName = $v['name'];
|
||||
} else {
|
||||
$v['active'] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理返回结果
|
||||
return self::preReturnParams(
|
||||
$goods_original_amount,
|
||||
$goods_amount,
|
||||
$origin_dispatch_amount,
|
||||
$dispatch_amount,
|
||||
$total_amount,
|
||||
$total_fee,
|
||||
$discount_fee,
|
||||
$coupon_fee,
|
||||
$activity_discount_money,
|
||||
$dispatch_discount_money,
|
||||
$activity_type,
|
||||
$score_amount,
|
||||
$new_goods_list,
|
||||
$need_address,
|
||||
$activity_discount_infos,
|
||||
$user_coupons,
|
||||
$user_address,
|
||||
$invoice_amount,
|
||||
$deliveryFee,
|
||||
$expressList,
|
||||
$expressName
|
||||
);
|
||||
}
|
||||
|
||||
// 获取可用优惠券列表
|
||||
public static function coupons($params, $goods_amount = 0)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
$order_type = $order_type ?? 'goods';
|
||||
$groupon_id = $groupon_id ?? 0; // 拼团的 团 id
|
||||
$buy_type = $buy_type ?? 'alone'; // 拼团的 购买方式: alone=单独购买,groupon=开团
|
||||
|
||||
// 商品总金额
|
||||
if (!$goods_amount) {
|
||||
// 下单的时候把计算好的 goods_amount 传进来了,接口直接获取可用列表的时候,需要计算
|
||||
foreach ($goods_list as $k => $goods) {
|
||||
$goods_amount += ($goods['goods_price'] * $goods['goods_num']);
|
||||
}
|
||||
}
|
||||
|
||||
$coupons = Coupons::getCouponsList(Coupons::COUPONS_CAN_USE);
|
||||
|
||||
$new_coupons = [];
|
||||
// 过滤,如果有一个产品不适用,则优惠券不允许使用,不显示
|
||||
foreach ($coupons as $key => $coupon) {
|
||||
if ($coupon['goods_ids'] === '0') {
|
||||
// 所有商品可用
|
||||
$can_use = true;
|
||||
} else {
|
||||
$goods_ids = explode(',', $coupon['goods_ids']);
|
||||
|
||||
$can_use = true;
|
||||
foreach ($goods_list as $k => $goods) {
|
||||
if (!in_array($goods['goods_id'], $goods_ids)) {
|
||||
$can_use = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 商品可用 并且 商品金额满足
|
||||
if ($can_use && $coupon->enough <= $goods_amount) {
|
||||
$new_coupons[] = $coupon;
|
||||
}
|
||||
}
|
||||
|
||||
$new_coupons = array_values($new_coupons);
|
||||
|
||||
return $new_coupons;
|
||||
}
|
||||
|
||||
public static function createOrder($params)
|
||||
{
|
||||
$user = User::info();
|
||||
|
||||
// 入参
|
||||
extract($params);
|
||||
$remark = $remark ?? null;
|
||||
$order_type = $order_type ?? 'goods';
|
||||
$groupon_id = $groupon_id ?? 0; // 拼团的 团 id
|
||||
$buy_type = $buy_type ?? 'alone'; // 拼团的 购买方式: alone=单独购买,groupon=开团
|
||||
$invoice = $invoice ?? null; // 发票申请
|
||||
|
||||
// 订单计算数据
|
||||
extract(self::pre($params, "create"));
|
||||
|
||||
$order = Db::transaction(function () use (
|
||||
$user,
|
||||
$order_type,
|
||||
$groupon_id,
|
||||
$buy_type,
|
||||
$goods_original_amount,
|
||||
$goods_amount,
|
||||
$dispatch_amount,
|
||||
$total_amount,
|
||||
$score_amount,
|
||||
$total_fee,
|
||||
$discount_fee,
|
||||
$coupon_fee,
|
||||
$activity_discount_money,
|
||||
$dispatch_discount_money,
|
||||
$activity_discount_infos,
|
||||
$new_goods_list,
|
||||
$activity_type,
|
||||
$user_coupons,
|
||||
$user_address,
|
||||
$remark,
|
||||
$from,
|
||||
$invoice,
|
||||
$invoice_amount,
|
||||
$deliveryFee,
|
||||
$expressName
|
||||
) {
|
||||
// 订单创建前
|
||||
$data = [
|
||||
'user' => $user,
|
||||
'order_type' => $order_type,
|
||||
'groupon_id' => $groupon_id,
|
||||
'buy_type' => $buy_type,
|
||||
'goods_original_amount' => $goods_original_amount,
|
||||
'goods_amount' => $goods_amount,
|
||||
'dispatch_amount' => $dispatch_amount,
|
||||
'total_amount' => $total_amount,
|
||||
'score_amount' => $score_amount,
|
||||
'total_fee' => $total_fee,
|
||||
'discount_fee' => $discount_fee,
|
||||
'coupon_fee' => $coupon_fee,
|
||||
'activity_discount_money' => $activity_discount_money,
|
||||
'dispatch_discount_money' => $dispatch_discount_money,
|
||||
'activity_discount_infos' => $activity_discount_infos,
|
||||
'new_goods_list' => $new_goods_list,
|
||||
'activity_type' => $activity_type,
|
||||
'user_coupons' => $user_coupons,
|
||||
'user_address' => $user_address,
|
||||
'remark' => $remark,
|
||||
'from' => $from
|
||||
];
|
||||
|
||||
// 如果是抢购需看是否有名额才能下单
|
||||
$activityId = 0;
|
||||
if ($activity_type === 'robbuy') {
|
||||
$activityId = $new_goods_list[0]['detail']['activity']['id'];
|
||||
$id = RobOrderModel::where([
|
||||
'user_id' => $user->id,
|
||||
'activity_id' => $activityId,
|
||||
'goods_id' => $new_goods_list[0]['detail']['id'],
|
||||
'is_rob' => 1
|
||||
])->value('id');
|
||||
|
||||
if (empty($id)) {
|
||||
return [
|
||||
'create_result' => 401,
|
||||
'msg' => '你未获得抢购资格暂无法购买'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是活动,这里面减掉 redis 库存
|
||||
\think\Hook::listen('order_create_before', $data);
|
||||
|
||||
// 团信息, 如果是参与旧团拼团才会不为 null,(开新团放到支付成功)
|
||||
$groupon = cache('grouponinfo-' . $user->id);
|
||||
$groupon = $groupon ? json_decode($groupon, true) : null;
|
||||
// 立即清除缓存
|
||||
cache('grouponinfo-' . $user->id, NULL);
|
||||
|
||||
$orderData = [];
|
||||
$orderData['order_sn'] = self::getSn($user->id);
|
||||
$orderData['user_id'] = $user->id;
|
||||
$orderData['type'] = $order_type;
|
||||
$orderData['activity_type'] = $activity_type;
|
||||
$orderData['goods_amount'] = $goods_amount;
|
||||
$orderData['dispatch_amount'] = $dispatch_amount;
|
||||
$orderData['store_delivery_fee'] = $deliveryFee;
|
||||
$orderData['total_amount'] = $total_amount;
|
||||
$orderData['pay_fee'] = $total_amount;
|
||||
$orderData['score_amount'] = $score_amount;
|
||||
$orderData['total_fee'] = $total_fee;
|
||||
$orderData['discount_fee'] = $discount_fee;
|
||||
$orderData['score_fee'] = $score_amount; // 记录score 支付数
|
||||
$orderData['coupon_fee'] = $coupon_fee;
|
||||
$orderData['activity_discount_money'] = $activity_discount_money;
|
||||
$orderData['dispatch_discount_money'] = $dispatch_discount_money;
|
||||
$orderData['goods_original_amount'] = $goods_original_amount;
|
||||
$orderData['last_total_fee'] = 0;
|
||||
$orderData['activity_id'] = $activityId;
|
||||
$orderData['express_name'] = $expressName;
|
||||
|
||||
if ($user_address) {
|
||||
$orderData['phone'] = $user_address->phone;
|
||||
$orderData['consignee'] = $user_address->consignee;
|
||||
$orderData['province_name'] = $user_address->province_name;
|
||||
$orderData['city_name'] = $user_address->city_name;
|
||||
$orderData['area_name'] = $user_address->area_name;
|
||||
$orderData['address'] = $user_address->address;
|
||||
$orderData['province_id'] = $user_address->province_id;
|
||||
$orderData['city_id'] = $user_address->city_id;
|
||||
$orderData['area_id'] = $user_address->area_id;
|
||||
}
|
||||
|
||||
// 处理发票申请
|
||||
if ($invoice_amount > 0) {
|
||||
if (!empty($invoice) && $invoice['amount'] == $invoice_amount) {
|
||||
$orderData['invoice_status'] = 1; // 已申请
|
||||
} else {
|
||||
$orderData['invoice_status'] = 0; // 未申请
|
||||
}
|
||||
} else {
|
||||
$orderData['invoice_status'] = -1; // 不可开具发票
|
||||
|
||||
}
|
||||
$orderData['status'] = 0;
|
||||
$orderData['remark'] = $remark;
|
||||
$orderData['coupons_id'] = $user_coupons ? $user_coupons->id : 0;
|
||||
$orderData['platform'] = request()->header('platform');
|
||||
|
||||
$ext = $activity_discount_infos ? ['activity_discount_infos' => $activity_discount_infos] : []; // 促销活动信息
|
||||
$orderData['ext'] = json_encode($ext);
|
||||
$order = new Order();
|
||||
$order->allowField(true)->save($orderData);
|
||||
|
||||
// 将优惠券使用掉
|
||||
if ($user_coupons) {
|
||||
$user_coupons->use_order_id = $order->id;
|
||||
$user_coupons->usetime = time();
|
||||
$user_coupons->save();
|
||||
}
|
||||
|
||||
// 如果需要支付积分,扣除积分
|
||||
if ($score_amount) {
|
||||
// $user 为 Common\Auth 对象
|
||||
User::score(-$score_amount, $user->id, 'score_pay', $order->id, '', [
|
||||
'order_id' => $order->id,
|
||||
'order_sn' => $order->order_sn,
|
||||
]);
|
||||
}
|
||||
|
||||
// 添加发票数据
|
||||
if ($order->invoice_status == 1) {
|
||||
\addons\shopro\model\OrderInvoice::create([
|
||||
'order_id' => $order->id,
|
||||
'user_id' => $order->user_id,
|
||||
'amount' => $invoice['amount'],
|
||||
'type' => $invoice['type'],
|
||||
'header_name' => $invoice['header_name'],
|
||||
'tax_no' => $invoice['tax_no'],
|
||||
'mobile' => $invoice['mobile'],
|
||||
]);
|
||||
}
|
||||
|
||||
// 添加 订单 item
|
||||
foreach ($new_goods_list as $key => $buyinfo) {
|
||||
$detail = $buyinfo['detail'];
|
||||
$current_sku_price = $detail['current_sku_price'];
|
||||
|
||||
$orderItem = new OrderItem();
|
||||
|
||||
$orderItem->user_id = $user->id;
|
||||
$orderItem->order_id = $order->id;
|
||||
$orderItem->goods_id = $buyinfo['goods_id'];
|
||||
$orderItem->goods_type = $detail['type'];
|
||||
$orderItem->goods_sku_price_id = $buyinfo['sku_price_id'];
|
||||
|
||||
$item_activity_type = (isset($current_sku_price['activity_type']) && $current_sku_price['activity_type']) ? $current_sku_price['activity_type'] : '';
|
||||
$item_activity_type .= $buyinfo['activity_type'] ? ',' . $buyinfo['activity_type'] : '';
|
||||
$item_activity_type = trim($item_activity_type, ',');
|
||||
|
||||
$orderItem->activity_id = $current_sku_price['activity_id'] ?? 0; // 商品当前的活动类型
|
||||
$orderItem->activity_type = $item_activity_type ?: null; // 商品当前的活动类型
|
||||
// 当前商品规格对应的 活动下对应商品规格的 id
|
||||
$orderItem->item_goods_sku_price_id = isset($current_sku_price['item_goods_sku_price']) ?
|
||||
$current_sku_price['item_goods_sku_price']['id'] : 0;
|
||||
$orderItem->goods_sku_text = $current_sku_price['goods_sku_text'];
|
||||
$orderItem->goods_title = $detail->title;
|
||||
$orderItem->goods_image = empty($current_sku_price['image']) ? $detail->image : $current_sku_price['image'];
|
||||
$orderItem->goods_original_price = $detail->original_price;
|
||||
$orderItem->discount_fee = $buyinfo['discount_fee']; // 平均计算单件商品所享受的折扣
|
||||
$orderItem->pay_price = $buyinfo['pay_price']; // 平均计算单件商品不算运费,算折扣时候的金额
|
||||
$orderItem->goods_price = $detail->current_sku_price->price;
|
||||
$orderItem->goods_num = $buyinfo['goods_num'] ?? 1;
|
||||
$orderItem->goods_weight = $detail->current_sku_price->weight;
|
||||
$orderItem->dispatch_status = 0;
|
||||
$orderItem->dispatch_fee = $buyinfo['dispatch_amount'];
|
||||
$orderItem->dispatch_type = $buyinfo['dispatch_type'];
|
||||
$orderItem->dispatch_id = $buyinfo['dispatch_id'] ? $buyinfo['dispatch_id'] : 0;
|
||||
$orderItem->store_id = $buyinfo['store_id'] ? $buyinfo['store_id'] : 0;
|
||||
$orderItem->aftersale_status = 0;
|
||||
$orderItem->comment_status = 0;
|
||||
$orderItem->refund_status = 0;
|
||||
|
||||
$ext = [];
|
||||
if (isset($buyinfo['dispatch_date'])) {
|
||||
$ext['dispatch_date'] = $buyinfo['dispatch_date'];
|
||||
}
|
||||
if (isset($buyinfo['dispatch_phone'])) {
|
||||
$ext['dispatch_phone'] = $buyinfo['dispatch_phone'];
|
||||
}
|
||||
$orderItem->ext = json_encode($ext);
|
||||
$orderItem->save();
|
||||
}
|
||||
|
||||
// 订单创建后
|
||||
$data = [
|
||||
'user' => $user,
|
||||
'order' => $order,
|
||||
'from' => $from,
|
||||
'groupon' => $groupon,
|
||||
'buy_type' => $buy_type,
|
||||
'activity_type' => $activity_type,
|
||||
'new_goods_list' => $new_goods_list
|
||||
];
|
||||
\think\Hook::listen('order_create_after', $data);
|
||||
|
||||
// 重新获取订单
|
||||
$order = self::where('id', $order['id'])->find();
|
||||
$order['create_result'] = 200;
|
||||
|
||||
saveUserMessage([
|
||||
'user_id' => $user->id,
|
||||
'content' => "您成功创建了订单:订单号:{$order->order_sn}",
|
||||
'create_time' => time()
|
||||
]);
|
||||
|
||||
return $order;
|
||||
});
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 订单列表
|
||||
public static function getList($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
|
||||
$order = (new self())->where('user_id', $user->id)->with('item');
|
||||
|
||||
switch ($type) {
|
||||
case 'all':
|
||||
$order = $order;
|
||||
break;
|
||||
case 'nopay':
|
||||
$order = $order->nopay();
|
||||
break;
|
||||
case 'nosend':
|
||||
$order = $order->payed()->nosend();
|
||||
break;
|
||||
case 'noget':
|
||||
$order = $order->payed()->noget();
|
||||
break;
|
||||
case 'nocomment':
|
||||
$order = $order->payed()->nocomment();
|
||||
break;
|
||||
case 'aftersale':
|
||||
$order = $order->payed()->aftersale(); // 个人中心售后单不在走这里,而是直接走的售后单列表
|
||||
break;
|
||||
}
|
||||
|
||||
$orders = $order->order('id', 'desc')->paginate(10);
|
||||
|
||||
// 处理未支付订单 item status_code
|
||||
$orders = $orders->toArray();
|
||||
if ($orders['data']) {
|
||||
$data = $orders['data'];
|
||||
foreach ($data as $key => $od) {
|
||||
$data[$key] = self::setOrderItemStatusByOrder($od);
|
||||
}
|
||||
|
||||
$orders['data'] = $data;
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
// 订单详情
|
||||
public static function detail($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
|
||||
$order = (new self())->with('item')->where('user_id', $user->id);
|
||||
|
||||
if (isset($order_sn)) {
|
||||
$order = $order->where('order_sn', $order_sn);
|
||||
}
|
||||
if (isset($id)) {
|
||||
$order = $order->where('id', $id);
|
||||
}
|
||||
|
||||
$order = $order->find();
|
||||
|
||||
if (!$order) {
|
||||
new Exception('订单不存在');
|
||||
}
|
||||
|
||||
// 处理未支付订单 item status_code
|
||||
$order = self::setOrderItemStatusByOrder($order);
|
||||
|
||||
// 用户余额
|
||||
$order['user_money'] = $user->money;
|
||||
// 手机号码
|
||||
$order['user_mobile'] = hideStr($user->mobile, 3, 6);
|
||||
|
||||
// 组合付款金额
|
||||
$order['group_fee'] = bcsub($order['total_fee'], $user->money, 2);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 订单商品详情
|
||||
public static function itemDetail($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
$type = $type ?? 'default';
|
||||
|
||||
$order = (new self())->with(['item' => function ($query) use ($order_item_id) {
|
||||
$query->where('id', $order_item_id);
|
||||
}])->where('user_id', $user->id);
|
||||
|
||||
if (isset($order_sn)) {
|
||||
$order = $order->where('order_sn', $order_sn);
|
||||
}
|
||||
if (isset($id)) {
|
||||
$order = $order->where('id', $id);
|
||||
}
|
||||
|
||||
$order = $order->find();
|
||||
|
||||
if (!$order || !$order->item) {
|
||||
new Exception('订单不存在');
|
||||
}
|
||||
|
||||
// 处理未支付订单 item status_code
|
||||
$order = self::setOrderItemStatusByOrder($order);
|
||||
$item = $order['item'][0];
|
||||
unset($order['item']); // 移除订单表中的 item
|
||||
$item['order'] = $order; // 订单信息
|
||||
|
||||
if ($type == 'dispatch') {
|
||||
$store = null;
|
||||
$verifies = [];
|
||||
if (in_array($item['dispatch_type'], ['selfetch', 'store']) && $item['store_id']) {
|
||||
// 自提,商家配送
|
||||
|
||||
if (
|
||||
$item['dispatch_type'] == 'selfetch'
|
||||
&& $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_SENDED
|
||||
&& $item['refund_status'] <= 0
|
||||
) {
|
||||
// 关联核销码
|
||||
$verifies = Verify::where('order_id', $item['order_id'])
|
||||
->where('order_item_id', $item['id'])->select();
|
||||
}
|
||||
|
||||
$store = Store::where('id', $item['store_id'])->find();
|
||||
}
|
||||
$item['store'] = $store; // 门店信息
|
||||
$item['verify'] = $verifies; // 核销券列表
|
||||
|
||||
// $item['autosend']
|
||||
// 订单详情,自动发货内容 待补充
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
public static function operCancel($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
|
||||
$order = self::with('item')->where('user_id', $user->id)->where('id', $id)->nopay()->find();
|
||||
|
||||
if (!$order) {
|
||||
new Exception('订单不存在或已取消');
|
||||
}
|
||||
|
||||
// 订单取消
|
||||
$order = (new self)->doCancel($order, $user);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
public function doCancel($order, $user, $type = 'user')
|
||||
{
|
||||
$order = Db::transaction(function () use ($order, $user, $type) {
|
||||
$data = ['order' => $order];
|
||||
\think\Hook::listen('order_cancel_before', $data);
|
||||
|
||||
$order->status = Order::STATUS_CANCEL; // 取消订单
|
||||
$order->ext = json_encode($order->setExt($order, ['cancel_time' => time()])); // 取消时间
|
||||
$order->save();
|
||||
|
||||
OrderAction::operAdd($order, null, $user, $type, ($type == 'user' ? '用户' : '管理员') . '取消订单');
|
||||
|
||||
// 订单取消,退回库存,退回优惠券积分,等
|
||||
$data = ['order' => $order];
|
||||
\think\Hook::listen('order_cancel_after', $data);
|
||||
|
||||
return $order;
|
||||
});
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
private static function getItem($order, $order_item_id)
|
||||
{
|
||||
if (!$order) {
|
||||
new Exception('当前订单不存在');
|
||||
}
|
||||
|
||||
$order_item = null;
|
||||
foreach ($order->item as $item) {
|
||||
if ($item->id == $order_item_id) {
|
||||
$order_item = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$order_item) {
|
||||
new Exception('订单商品不需要操作');
|
||||
}
|
||||
|
||||
return $order_item;
|
||||
}
|
||||
|
||||
// 确认收货订单
|
||||
public static function operConfirm($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
|
||||
$order = Db::transaction(function () use ($id, $order_item_id, $user) {
|
||||
// 加锁查询订单,exist 里面的子查询不会加锁,但是该语句需要等待锁释放才能正常查询,所以下面的获取 item 已经是更改过状态之后的了
|
||||
$order = self::noget()->where('user_id', $user->id)->where('id', $id)->lock(true)->find();
|
||||
|
||||
// 获取要操作的 订单 item
|
||||
$item = self::getItem($order, $order_item_id);
|
||||
if ($item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND) {
|
||||
new Exception('当前订单未发货');
|
||||
}
|
||||
if ($item->dispatch_status == OrderItem::DISPATCH_STATUS_GETED) {
|
||||
new Exception('当前订单已收货');
|
||||
}
|
||||
|
||||
(new self())->getedItem($order, $item, ['oper_type' => 'user']);
|
||||
});
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 删除订单
|
||||
public static function operDelete($params)
|
||||
{
|
||||
$user = User::info();
|
||||
extract($params);
|
||||
|
||||
$order = self::canDelete()->where('user_id', $user->id)->where('id', $id)->find();
|
||||
|
||||
if (!$order) {
|
||||
new Exception('订单不存在或不可删除');
|
||||
}
|
||||
|
||||
$order = Db::transaction(function () use ($order, $user) {
|
||||
$order->delete(); // 删除订单
|
||||
|
||||
OrderAction::operAdd($order, null, $user, 'user', '用户删除订单');
|
||||
|
||||
return $order;
|
||||
});
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 评价
|
||||
public static function operComment($params)
|
||||
{
|
||||
$user = User::info();
|
||||
|
||||
$order = Db::transaction(function () use ($user, $params) {
|
||||
extract($params);
|
||||
|
||||
// 加锁读取订单,直到订单评价状态改变
|
||||
$order = self::with('item')->payed()->where('user_id', $user->id)->where('id', $id)->lock(true)->find();
|
||||
|
||||
// 获取要操作的 订单 item
|
||||
$item = self::getItem($order, $order_item_id);
|
||||
if ($item->comment_status == 1) {
|
||||
new Exception('当前商品已评价');
|
||||
}
|
||||
|
||||
// 订单评价前
|
||||
$data = ['order' => $order, 'item' => $item];
|
||||
\think\Hook::listen('order_comment_before', $data); // 重新拿到更新过的订单
|
||||
|
||||
$images = (isset($images) && $images) ? $images : null;
|
||||
|
||||
// 获取用户评论配置
|
||||
$config = \addons\shopro\model\Config::where('name', 'order')->value('value');
|
||||
$config = json_decode($config, true);
|
||||
|
||||
GoodsComment::create([
|
||||
'goods_id' => $item->goods_id,
|
||||
'order_id' => $order->id,
|
||||
'order_item_id' => $item->id,
|
||||
'user_id' => $user->id,
|
||||
'level' => $level,
|
||||
'content' => $content,
|
||||
'images' => $images ? implode(',', $images) : $images,
|
||||
'status' => !empty($config['user_reply']) ? 'show' : 'hidden'
|
||||
]);
|
||||
|
||||
$item->comment_status = OrderItem::COMMENT_STATUS_OK; // 评价成功
|
||||
$item->save();
|
||||
|
||||
OrderAction::operAdd($order, $item, $user, 'user', '用户评价成功');
|
||||
|
||||
// 订单评价后
|
||||
$data = ['order' => $order, 'item' => $item];
|
||||
\think\Hook::listen('order_comment_after', $data);
|
||||
|
||||
return $order;
|
||||
});
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 个人中心订单数量
|
||||
public static function statusNum()
|
||||
{
|
||||
$user = User::info();
|
||||
|
||||
$status_num['nopay'] = self::where('user_id', $user->id)->nopay()->count();
|
||||
$status_num['nosend'] = self::where('user_id', $user->id)->payed()->nosend()->count();
|
||||
$status_num['noget'] = self::where('user_id', $user->id)->payed()->noget()->count();
|
||||
$status_num['nocomment'] = self::where('user_id', $user->id)->payed()->nocomment()->count();
|
||||
// $status_num['aftersale'] = self::where('user_id', $user->id)->payed()->aftersale()->count();
|
||||
$status_num['aftersale'] = \addons\shopro\model\OrderAftersale::where('user_id', $user->id)->count();
|
||||
|
||||
return $status_num;
|
||||
}
|
||||
|
||||
public function paymentProcess($order, $notify)
|
||||
{
|
||||
$order->status = Order::STATUS_PAYED;
|
||||
$order->paytime = time();
|
||||
$order->transaction_id = $notify['transaction_id'];
|
||||
$order->payment_json = $notify['payment_json'];
|
||||
$order->pay_type = $notify['pay_type'];
|
||||
$order->pay_fee = $notify['pay_fee'];
|
||||
|
||||
// 组合支付更新付款金额
|
||||
if ($order->pay_type == 'group') {
|
||||
$order->total_fee = bcsub($order->total_fee, $order->pay_money, 2);
|
||||
}
|
||||
|
||||
$order->save();
|
||||
|
||||
saveUserMessage([
|
||||
'user_id' => $order->user_id,
|
||||
'content' => "付款成功,订单号:{$order->order_sn}",
|
||||
'create_time' => time()
|
||||
]);
|
||||
|
||||
$user = User::where('id', $order->user_id)->find();
|
||||
OrderAction::operAdd($order, null, $user, 'user', '用户支付成功');
|
||||
|
||||
// 组合支付扣除用户余额
|
||||
if ($order->pay_type == 'group') {
|
||||
User::where(['id' => $user->id])->setDec('money', $order->pay_money);
|
||||
}
|
||||
|
||||
// 预约抢购标记成已使用
|
||||
if ($order->activity_type === 'robbuy') {
|
||||
RobOrderModel::where([
|
||||
'user_id' => $user->id,
|
||||
'activity_id' => $order->activity_id,
|
||||
'goods_id' => OrderItem::where(['order_id' => $order->id])->value('goods_id')
|
||||
])->update(['is_use' => 1]);
|
||||
}
|
||||
|
||||
// 支付成功后续使用异步队列处理
|
||||
\think\Queue::push('\addons\shopro\job\OrderPayed@payed', ['order' => $order, 'user' => $user], 'shopro-high');
|
||||
return $order;
|
||||
}
|
||||
|
||||
// 开始退款
|
||||
public static function startRefund($order, $item, $refund_money, $user = null, $remark = '')
|
||||
{
|
||||
// 订单退款前
|
||||
$data = ['order' => $order, 'item' => $item];
|
||||
\think\Hook::listen('order_refund_before', $data);
|
||||
|
||||
$item->refund_status = \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_OK; // 同意退款
|
||||
$item->refund_fee = bcadd($item->refund_fee, $refund_money, 2);;
|
||||
$item->save();
|
||||
|
||||
\addons\shopro\model\OrderAction::operAdd($order, $item, $user, ($user ? 'admin' : 'system'), $remark . ',退款金额:' . $refund_money);
|
||||
|
||||
\app\admin\model\shopro\order\Order::refund($order, $item, $refund_money, $remark);
|
||||
|
||||
// 订单退款后
|
||||
$data = ['order' => $order, 'item' => $item];
|
||||
\think\Hook::listen('order_refund_after', $data);
|
||||
}
|
||||
|
||||
|
||||
// 退款
|
||||
public static function refund($order, $item, $refund_money, $remark = '')
|
||||
{
|
||||
// 生成退款单
|
||||
$refundLog = new RefundLog();
|
||||
$refundLog->order_sn = $order->order_sn;
|
||||
$refundLog->refund_sn = RefundLog::getSn($order->user_id);
|
||||
$refundLog->order_item_id = $item->id;
|
||||
$refundLog->pay_fee = $order->pay_fee;
|
||||
$refundLog->refund_fee = $refund_money;
|
||||
$refundLog->pay_type = $order->pay_type;
|
||||
$refundLog->save();
|
||||
|
||||
if ($order->pay_type == 'wechat' || $order->pay_type == 'alipay') {
|
||||
// 微信|支付宝退款
|
||||
|
||||
// 退款数据
|
||||
$order_data = [
|
||||
'out_trade_no' => $order->order_sn
|
||||
];
|
||||
|
||||
if ($order->pay_type == 'wechat') {
|
||||
$total_fee = $order->pay_fee * 100;
|
||||
$refund_fee = $refund_money * 100;
|
||||
|
||||
$order_data = array_merge($order_data, [
|
||||
'out_refund_no' => $refundLog->refund_sn,
|
||||
'total_fee' => $total_fee,
|
||||
'refund_fee' => $refund_fee,
|
||||
'refund_desc' => $remark,
|
||||
]);
|
||||
} else {
|
||||
$order_data = array_merge($order_data, [
|
||||
'out_request_no' => $refundLog->refund_sn,
|
||||
'refund_amount' => $refund_money,
|
||||
]);
|
||||
}
|
||||
|
||||
$notify_url = request()->domain() . '/addons/shopro/pay/notifyr/payment/' . $order->pay_type . '/platform/' . $order->platform;
|
||||
|
||||
$pay = new \addons\shopro\library\PayService($order->pay_type, $order->platform, $notify_url);
|
||||
$result = $pay->refund($order_data);
|
||||
|
||||
\think\Log::write('refund-result' . json_encode($result));
|
||||
|
||||
|
||||
if ($order->pay_type == 'wechat') {
|
||||
// 微信通知回调 pay->notifyr
|
||||
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
|
||||
return true;
|
||||
} else {
|
||||
throw new \Exception($result['return_msg']);
|
||||
}
|
||||
} else {
|
||||
// 支付宝通知回调 pay->notifyx
|
||||
if ($result['code'] == "10000") {
|
||||
return true;
|
||||
} else {
|
||||
throw new \Exception($result['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
// { // 微信返回结果
|
||||
// "return_code":"SUCCESS",
|
||||
// "return_msg":"OK",
|
||||
// "appid":"wx39cd0799d4567dd0",
|
||||
// "mch_id":"1481069012",
|
||||
// "nonce_str":"huW9eIAb5BDPn0Ma",
|
||||
// "sign":"250316740B263FE53F5DFF50AF5A8FA1",
|
||||
// "result_code":"SUCCESS",
|
||||
// "transaction_id":"4200000497202004072822298902",
|
||||
// "out_trade_no":"202010300857029180027000",
|
||||
// "out_refund_no":"1586241595",
|
||||
// "refund_id":"50300603862020040700031444448",
|
||||
// "refund_channel":[],
|
||||
// "refund_fee":"1",
|
||||
// "coupon_refund_fee":"0",
|
||||
// "total_fee":"1",
|
||||
// "cash_fee":"1",
|
||||
// "coupon_refund_count":"0",
|
||||
// "cash_refund_fee":"1
|
||||
// }
|
||||
|
||||
// { // 支付宝返回结果
|
||||
// "code": "10000",
|
||||
// "msg": "Success",
|
||||
// "buyer_logon_id": "157***@163.com",
|
||||
// "buyer_user_id": "2088902485164146",
|
||||
// "fund_change": "Y",
|
||||
// "gmt_refund_pay": "2020-08-15 16:11:45",
|
||||
// "out_trade_no": "202002460317545607015300",
|
||||
// "refund_fee": "0.01",
|
||||
// "send_back_fee": "0.00",
|
||||
// "trade_no": "2020081522001464141438570535"
|
||||
// }
|
||||
} else if ($order->pay_type == 'wallet') {
|
||||
// 余额退款
|
||||
if ($refund_money != 0) {
|
||||
\addons\shopro\model\User::money($refund_money, $order->user_id, 'wallet_refund', $order->id, '', [
|
||||
'order_id' => $order->id,
|
||||
'order_sn' => $order->order_sn,
|
||||
'item_id' => $item->id,
|
||||
]);
|
||||
}
|
||||
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
|
||||
return true;
|
||||
} else if ($order->pay_type == 'score') {
|
||||
// 积分退款,暂不支持积分退款
|
||||
}
|
||||
}
|
||||
|
||||
public static function refundFinish($order, $item, $refundLog)
|
||||
{
|
||||
// 退款完成
|
||||
$refundLog->status = 1;
|
||||
$refundLog->save();
|
||||
|
||||
// 退款完成
|
||||
$item->refund_status = \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_FINISH; // 退款完成
|
||||
$item->save();
|
||||
\addons\shopro\model\OrderAction::operAdd($order, $item, null, 'admin', '退款成功');
|
||||
}
|
||||
|
||||
|
||||
public function setExt($order, $field, $origin = [])
|
||||
{
|
||||
$newExt = array_merge($origin, $field);
|
||||
|
||||
$orderExt = $order['ext_arr'];
|
||||
|
||||
return array_merge($orderExt, $newExt);
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户