| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- <?php
- namespace addons\Course\common\models;
- use addons\Course\common\enums\CourseEnum;
- use addons\Course\common\events\order\CourseOrderPaidEvent;
- use addons\JoinPay\common\services\PaymentService;
- use common\enums\PaymentTradeOrderEnum;
- use common\enums\StatusEnum;
- use common\models\mall\MallAddons;
- use common\models\user\User;
- use Yii;
- use common\enums\PayTypeEnum;
- use common\logic\wechat\WechatMiniProgramLogic;
- use common\models\common\PaymentTrade;
- use common\models\common\PaymentTradeOrder;
- use common\models\order\Order;
- use common\models\user\UserInfo;
- use Exception;
- /**
- * This is the model class for table "{{%addons_course_orders}}".
- *
- * @property int $id
- * @property int $mall_id 商城id
- * @property int $user_id
- * @property int $course_id 课程id
- * @property string $order_no 订单编号
- * @property float $price 价格
- * @property float $pay_money 实际付款金额
- * @property int $pay_type
- * @property int $use_discount 是否使用折扣,1:是,0:否
- * @property int $discount_type 使用的折扣类型:0:未使用,1:积分,2:红包,3:活动打折,4:会员等级优惠,5:优惠劵,跟 use_discount 字段相关联,只有当 use_discount = 1 时,此字段的值有意义
- * @property float $discount_quota 折扣金额,跟 use_discount 字段相关联,只有当 use_discount = 1 时,此字段的值有意义
- * @property int $order_status 订单状态,1:未支付,2:已取消,3:已支付,4:待退款(会员已发起退款申请但平台未处理),5:退款完成,6:订单完成(从订单支付完成的时间点起,已过15天)
- * @property int $pay_time 支付时间
- * @property int $cancel_time 订单取消的时间,下单后未支付,经过一定的时间系统自动取消
- * @property string $remark 备注
- * @property int $status 状态 0禁用,1启用,-1是删除
- * @property int $created_at
- * @property int $updated_at
- */
- class AddonsCourseOrders extends \common\models\base\BaseActiveRecord
- {
- /**
- * {@inheritdoc}
- */
- public static function tableName()
- {
- return '{{%addons_course_orders}}';
- }
- /**
- * {@inheritdoc}
- */
- public function rules()
- {
- return [
- [['mall_id', 'user_id', 'course_id', 'pay_type', 'use_discount', 'discount_type', 'order_status', 'pay_time', 'cancel_time', 'status', 'created_at', 'updated_at', 'refund_at', 'refund_status'], 'integer'],
- [['course_id'], 'required'],
- [['price', 'pay_money', 'discount_quota'], 'number'],
- [['order_no'], 'string', 'max' => 50],
- [['remark', 'refund_desc'], 'string', 'max' => 255],
- ];
- }
- /**
- * {@inheritdoc}
- */
- public function attributeLabels()
- {
- return [
- 'id' => 'ID',
- 'mall_id' => '商城id',
- 'user_id' => 'User ID',
- 'course_id' => '课程id',
- 'order_no' => '订单编号',
- 'price' => '价格',
- 'pay_money' => '实际付款金额',
- 'pay_type' => 'Pay Type',
- 'use_discount' => '是否使用折扣,1:是,0:否',
- 'discount_type' => '使用的折扣类型:0:未使用,1:积分,2:红包,3:活动打折,4:会员等级优惠,5:优惠劵,跟 use_discount 字段相关联,只有当 use_discount = 1 时,此字段的值有意义',
- 'discount_quota' => '折扣金额,跟 use_discount 字段相关联,只有当 use_discount = 1 时,此字段的值有意义',
- 'order_status' => '订单状态,1:未支付,2:已取消,3:已支付,4:待退款(会员已发起退款申请但平台未处理),5:退款完成,6:订单完成(从订单支付完成的时间点起,已过15天)',
- 'pay_time' => '支付时间',
- 'cancel_time' => '订单取消的时间,下单后未支付,经过一定的时间系统自动取消',
- 'remark' => '备注',
- 'status' => '状态 0禁用,1启用,-1是删除',
- 'created_at' => 'Created At',
- 'updated_at' => 'Updated At',
- ];
- }
- public function getUser(){
- return $this->hasOne(User::class, ['id' => 'user_id']);
- }
- public function getCourse(){
- return $this->hasOne(AddonsCourse::class, ['id' => 'course_id']);
- }
- public static function setData(array $data)
- {
- $model = new self();
- $model->load($data, '');
- $model->status = StatusEnum::ENABLED;
- if (!$model->save()) {
- self::$static_error = '保存数据失败:' . $model->getErrorMessage();
- return false;
- }
- return $model;
- }
- /**
- * 新增用户购买的课程
- *
- * @return boolean
- * @throws \Exception
- */
- public function addBought($id, $user_id)
- {
- $bought = AddonsCourseBought::findOne(["order_id" => $id, "buy_type" => 1, "user_id" => $user_id]);
- if (empty($bought)) {
- $bought = new AddonsCourseBought();
- $bought->mall_id = $this->mall_id;
- $bought->user_id = $this->user_id;
- $bought->course_id = $this->course_id;
- $bought->amount = $this->real_pay;
- $bought->buy_type = 1;
- $bought->order_id = $this->id;
- $bought->status = 2;
- if (!$bought->save()) throw new \Exception("保存课程失败");
- }
- return true;
- }
- public static function generateOrderNo($prefix = '')
- {
- $randLen = 6;
- $id = base_convert(substr(uniqid(), 0 - $randLen), 16, 10);
- if (strlen($id) > 10) {
- $id = substr($id, -10);
- } elseif (strlen($id) < 10) {
- $rLen = 10 - strlen($id);
- $id = $id . rand(pow(10, $rLen - 1), pow(10, $rLen) - 1);
- }
- $dateTimeStr = date('YmdHis');
- return $prefix . $dateTimeStr . $id;
- }
- /**
- * @throws \Exception
- */
- public static function preview($user, $mall_id, $post)
- {
- return self::handleData($user, $mall_id, $post, 1);
- }
- public static function createOrder($user, $mall_id, $params)
- {
- $t = \Yii::$app->db->beginTransaction();
- try {
- if (Order::isStopOrder($mall_id)) throw new Exception('站点维护中,暂时无法下单');
- $data = self::handleData($user, $mall_id, $params, 2);
- //生成订单
- $orderno = AddonsCourseOrders::generateOrderNo('C');
- $oldOrder = AddonsCourseOrders::getOne([['order_no' => $orderno]]);
- if ($oldOrder) {
- self::$static_error = '重复下单';
- return false;
- }
- $use_discount = 0;
- $discount_type = 0;
- foreach ($data['list'] as $orderItem) {
- if (!empty($orderItem['use_discount'])) {
- $use_discount = 1;
- $discount_type = 4;
- }
- }
- $params = [
- 'mall_id' => $mall_id,
- 'user_id' => $user['id'],
- 'course_id' => $data['list'][0]['id'],
- 'order_no' => $orderno,
- 'price' => $data['total_price'],
- 'pay_money' => $data['total_price'],
- 'remark' => isset($post["remark"]) ? $post["remark"] : "",
- 'pay_type' => CourseEnum::PAY_TYPE_NO_PAY,
- 'order_status' => CourseEnum::STATUS_WAIT_PAY,
- 'use_discount' => $use_discount,
- 'discount_type' => $discount_type,
- 'discount_quota' => !empty($data['discount_quota']) ? $data['discount_quota'] : 0.00,
- ];
- $state = AddonsCourseOrders::setData($params);
- if (!$state) {
- throw new \Exception("订单保存失败");
- }
- AddonsCourseBought::addBought($params, $state->id);
- $order_trade_info[] = [
- 'order_no' => $orderno,
- 'amount' => $data['total_price'],
- 'title' => '购买课程' . mb_strlen($data['list'][0]['name']) > 32 ? mb_substr($data['list'][0]['name'], 0, 32) : $data['list'][0]['name'],
- 'notify_class' => CourseEnum::NOTIFY_CLASS,
- 'order_type' => PaymentTradeOrderEnum::COURSE_ORDER,
- ];
- $t->commit();
- //生成支付流水
- $res = \Yii::$app->services->pay->createTrade($params['mall_id'], $params['user_id'], $order_trade_info);
- if ($res == false) throw new \Exception('生成支付流水失败');
- $arr['transaction'] = $res;
- $arr['orderno'] = $orderno;
- $arr['pay_success_page'] = Yii::$app->services->setting->mall->get($params['mall_id'], 'order.pay_after_link');// 支付成功后跳转的地址
- return $arr;
- } catch (\Exception $exception) {
- $t->rollBack();
- self::$static_error = $exception->getMessage();
- return false;
- }
- }
- /**
- * 支付
- * User: wniu
- * Date: 2022/3/18
- * Time: 3:49 下午
- * *
- * @param $params
- * @param $pay_type
- * @return AddonsCourseOrders|false
- */
- public static function pay($params, $pay_type)
- {
- $trans = Yii::$app->db->beginTransaction();
- try {
- $model = self::findOne(['id' => $params['id'], 'status' => [StatusEnum::ENABLED]]);
- if (empty($model)) throw new \Exception('订单不存在或已删除');
- if ($model->order_status != CourseEnum::STATUS_WAIT_PAY) throw new \Exception('订单已经被处理,无法进行支付');
- $model->order_status = CourseEnum::IS_PAY_YES;
- $model->pay_type = $pay_type;
- $model->pay_time = time();
- $res = $model->save();
- $course = AddonsCourse::getOne([['id' => $params['course_id']]]);
- if ($course) {
- $course->sales = $course->sales + 1;
- $course->save();
- }
- $bought = AddonsCourseBought::getOne([['order_id' => $params['id']]]);
- if ($bought) {
- $bought->is_pay = CourseEnum::IS_PAY_YES;
- $bought->save();
- }
- if ($res === false) throw new \Exception('修改订单失败:' . $model->getErrorMessage());
- $trans->commit();
- // 触发订单支付事件,事务提交完成后触发
- $event = new CourseOrderPaidEvent($model['mall_id']);
- $order['id'] = $model['id'];
- $order['operator_id'] = $params['operator_id'] ?? '';
- $order['operator_name'] = $params['operator_name'] ?? '';
- Yii::$app->services->events->dispatch($event, $order, $event->listeners);
- return $model;
- } catch (\Exception $e) {
- if (isset($trans)) $trans->rollback();
- self::$static_error = $e->getMessage();
- return false;
- }
- }
- /**
- * @param int $type 1 预览订单 2:提交订单
- * * @param $form_data
- * @param int $type
- * @throws \Exception
- */
- protected static function handleData($user, $mall_id, $form_data, $type = 1)
- {
- $listData = self::getCourseListData($form_data["list"], $user);
- $total_price = 0.00;
- $total_original_price = 0.00;
- foreach ($listData as &$priceItem) {
- $total_price += $priceItem['price'];
- $total_original_price += $priceItem['original_price'];
- }
- $discount_quota = 0.00;
- if ($total_original_price > $total_price) {
- $discount_quota = bcsub($total_original_price, $total_price, 2);
- }
- return [
- 'list' => $listData,
- 'total_price' => self::price_format($total_price),
- 'total_original_price' => self::price_format($total_original_price),
- 'discount_quota' => self::price_format($discount_quota),
- ];
- }
- /**
- * 课程列表数据
- * @param $courseList
- * @return array
- * @throws \Exception
- */
- protected static function getCourseListData($courseList, $user)
- {
- $list = [];
- if (!empty($courseList)) {
- foreach ($courseList as $i => $item) {
- $list[] = self::getOneCourseItemData($item, $user);
- }
- }
- return $list;
- }
- /**
- * 单个课程数据
- * @return array
- * @throws \Exception
- */
- protected static function getOneCourseItemData($item, $user)
- {
- $course = AddonsCourse::getOne([['id' => $item['id'], 'status' => StatusEnum::ENABLED]]);
- if (!$course) {
- throw new \Exception('课程不存在或已下架。');
- }
- //是否使用了折扣
- $use_discount = false;
- //会员价
- $price = $course->getMemberPrice($user, $use_discount);
- $itemData = [
- 'id' => $course->id,
- 'name' => $course->name,
- 'num' => $item['num'],
- 'price' => self::price_format($price * $item['num']),
- 'logo' => $course->logo,
- 'original_price' => self::price_format($course->price * $item['num']),
- 'use_discount' => $use_discount,
- 'line_off_price' => $course->line_off_price,
- ];
- return $itemData;
- }
- static function price_format($val, $decimals = 2)
- {
- $val = floatval($val);
- $result = number_format($val, $decimals, '.', '');
- return $result;
- }
- /**
- * 上传物流信息(微信小程序要求)
- * @param $order
- * @return true|void
- *
- */
- public static function uploadShippingInfo($order)
- {
- $order_no = $order['order_no'];
- $mall_id = $order['mall_id'];
- $course = $order['course'];
- try {
- $payment_trade_order_model = PaymentTradeOrder::findOne(['order_no'=>$order_no,'is_pay'=>1]);
- if(empty($payment_trade_order_model)) throw new \Exception("order_no:".$order_no.",payment_trade_order_model不存在");
- $pay_type = [PayTypeEnum::WECHAT, PayTypeEnum::JOINPAY, PayTypeEnum::HUIFUPAY, PayTypeEnum::UNIONPAY];
- $trade_type = ['JSAPI', 'WEIXIN_XCX', 'T_MINIAPP', 'W06-CASHIER', 'W06'];
- $payment_trade_model = PaymentTrade::findOne(['id'=>$payment_trade_order_model['trade_id'],'pay_type'=>$pay_type,'trade_type'=>$trade_type]);
- if(empty($payment_trade_model)) throw new \Exception("trade_id:".$payment_trade_order_model['trade_id'].",payment_trade_model不存在");
- if($payment_trade_model['is_pay_group']){
- //组合支付,查询对应的子流水
- $payment_trade_model = PaymentTrade::findOne(['parent_trade_id'=>$payment_trade_order_model['trade_id'],'pay_type'=>$pay_type,'trade_type'=>$trade_type]);
- }else{
- $payment_trade_model = PaymentTrade::findOne(['id'=>$payment_trade_order_model['trade_id'],'pay_type'=>$pay_type,'trade_type'=>$trade_type]);
- }
- $third_trade_no = '';
- switch ($payment_trade_model['pay_type']){
- case PayTypeEnum::WECHAT:
- //微信支付
- $third_trade_no = $payment_trade_model['third_trade_no'];
- break;
- case PayTypeEnum::JOINPAY:
- if(!MallAddons::isInstall($mall_id,'JoinPay')){
- return true;
- }
- $third_trade_no = $payment_trade_model['second_trade_no'];
- break;
- case PayTypeEnum::HUIFUPAY:
- if(!MallAddons::isInstall($mall_id,'HuifuPay')){
- return true;
- }
- $third_trade_no = $payment_trade_model['second_trade_no'];
- break;
- }
- if(empty($third_trade_no)){
- return true;
- }
- $params['mall_id'] = $mall_id;
- $params['item_desc'] = $course['name']??'';
- $params['user_id'] = $payment_trade_model['user_id'];
- $params['third_trade_no'] = $third_trade_no;
- $params['delivery_mode'] = 1;
- $params['is_all_delivered'] = false;
- WechatMiniProgramLogic::uploadShippingInfoWechatByVirtually($params);
- }catch (\Exception $e){
- Yii::error('uploadShippingInfo:' . $e->getMessage());
- }
- }
- }
|