user->identity; $orderNos = []; $amount = 0; $title = ''; foreach ($paymentOrders as $paymentOrder) { $orderNos[] = $paymentOrder->orderNo; $amount = $amount + $paymentOrder->amount; $title = $title . str_replace(';', '', $paymentOrder->title) . ';'; } sort($orderNos); $orderNos[] = $amount; $unionOrderNo = UniqueOrderNo::generate(UniqueOrderNo::ORDER_TYPE_PAYMENT_ORDER_UNION, "\\app\\models\\PaymentOrderUnion"); $title = mb_substr($title, 0, 32); $paymentOrderUnion = new PaymentOrderUnion(); $paymentOrderUnion->cx_mch_id = $cx_mch_id; $paymentOrderUnion->user_id = $user->id; $paymentOrderUnion->order_no = $unionOrderNo; $paymentOrderUnion->out_trade_no = $unionOrderNo; $paymentOrderUnion->amount = $amount; $paymentOrderUnion->title = $title; foreach ($paymentOrders as $paymentOrder) { $supportPayTypes = $paymentOrder->supportPayTypes; if ($supportPayTypes) { $supportPayTypes = (array)$supportPayTypes; } } if (!empty($supportPayTypes) && is_array($supportPayTypes)) { $appendPayTypes = []; foreach ($supportPayTypes as $index => $payType) { if ($payType == 'online_pay') { $appendPayTypes[] = SysConst::$cxPayTypeEnWxpay; $appendPayTypes[] = SysConst::$cxPayTypeEnAlipay; unset($supportPayTypes[$index]); break; } } $supportPayTypes = array_merge($supportPayTypes, $appendPayTypes); } $paymentOrderUnion->support_pay_types = $paymentOrderUnion->encodeSupportPayTypes($supportPayTypes); $t = \Yii::$app->db->beginTransaction(); try { if (!$paymentOrderUnion->save()) { return (new Model())->getModelError($paymentOrderUnion); } foreach ($paymentOrders as $paymentOrder) { $model = new \app\models\PaymentOrder(); $model->payment_order_union_id = $paymentOrderUnion->id; $model->order_no = $paymentOrder->orderNo; $model->amount = $paymentOrder->amount; $model->title = $paymentOrder->title; $model->notify_class = $paymentOrder->notifyClass; $model->notify_status = 0; if (!$model->save()) { return (new Model())->getModelError($model); } } $t->commit(); } catch (\Exception $e) { $t->rollBack(); return Model::asReturnError($e->getMessage()); } return Model::asReturnSuccess('ok',['pay_id' => $paymentOrderUnion->id]); } /** * 获取支付数据 * @param integer $id 合并支付ID * @param string $payType 支付类型 * @param integer $cx_mch_id 平台商户ID,默认0 * @param string $platform 平台类型,默认微信小程序(wxmp) * return array */ public function getPayData($id, $payType, $cx_mch_id = 0, $openid = '',$platform = 'wxmp') { if (\Yii::$app->user->isGuest) { return Model::asReturnError('用户未登录。'); } /** @var User $user */ $user = \Yii::$app->user->identity; $paymentOrderUnion = PaymentOrderUnion::findOne(['id' => $id, 'user_id' => $user->id]); if (!$paymentOrderUnion) { return Model::asReturnError('待支付订单不存在。'); } if ($paymentOrderUnion->is_pay == 1) { return Model::asReturnError('订单已经支付。'); } $supportPayTypes = (array)$paymentOrderUnion->decodeSupportPayTypes($paymentOrderUnion->support_pay_types); if (!empty($supportPayTypes) && is_array($supportPayTypes) && !in_array($payType, $supportPayTypes)) { if ($paymentOrderUnion->amount != 0) { // 订单金额为0时使用余额支付 return Model::asReturnError('该订单不支持此支付方式。'); } } //订单金额为0时,使用余额支付 if($paymentOrderUnion->amount == 0 && $payType != SysConst::$cxPayTypeEnBalance){ return Model::asReturnError('该订单只支持余额支付'); } switch ($payType) { case SysConst::$cxPayTypeEnBalance: $data = [ 'pay_type' => $payType, 'id' => $paymentOrderUnion->id, 'balance_amount' => $user->balance ? $user->balance->account_balance : 0.00, 'order_amount' => $paymentOrderUnion->amount, ]; break; case SysConst::$cxPayTypeEnWxpay: $plugin = new \app\models\common\PluginService(); if($user->bindWxmp){ if(!empty($openid)){ $params['openid'] = $openid; }else{ $params['openid'] = $user->bindWxmp->openid; } $wxmpService = $plugin->getWxmpService($cx_mch_id); $res = $wxmpService->pay->unifiedOrder(array_merge([ 'body' => $paymentOrderUnion->title, 'out_trade_no' => $paymentOrderUnion->order_no, 'total_fee' => $paymentOrderUnion->amount * 100, 'notify_url' => $this->getNotifyUrl(SysConst::$cxPayTypeEnWxpay), 'trade_type' => 'JSAPI', ],$params)); $appPayData = [ 'appId' => $wxmpService->appId, 'timeStamp' => (string)time(), 'nonceStr' => md5(uniqid()), 'package' => 'prepay_id=' . $res['prepay_id'], 'signType' => 'MD5', ]; $appPayData['paySign'] = $wxmpService->pay->makeSign($appPayData); } else { return Model::asReturnError('账户尚未绑定微信,该订单不支持此付款方式'); } $data = array_merge([ 'pay_type' => $payType, 'id' => $paymentOrderUnion->id, ],$appPayData); break; case SysConst::$cxPayTypeEnUnionpay: $plugin = new \app\models\common\PluginService(); $updcService = $plugin->getUpdcService($cx_mch_id); if($paymentOrderUnion->out_trade_no == $paymentOrderUnion->order_no){ $merOrderNo = $updcService->getMerOrderNo($cx_mch_id); $paymentOrderUnion->out_trade_no = $merOrderNo; if (!$paymentOrderUnion->save()) { return (new Model())->getModelError($paymentOrderUnion); } } $merOrderNo = $paymentOrderUnion->out_trade_no; if($platform == SysConst::$cxPlatformWxmp){ $appPayData = []; //银联微信小程序支付 if($user->bindWxmp){ $paymentOrderUnion->is_pay = 1; $paymentOrderUnion->pay_type = SysConst::$cxPayTypeUnionpay; $paymentOrderUnion->updated_at = time(); $chnlMerOrdrNo = 0; $paymentOrderUnion->transaction_id = $chnlMerOrdrNo; if ($paymentOrderUnion->save()) { //支付完成之后,相关的操作 $form = new PaymentNotify(); $resp = $form->notify($paymentOrderUnion); if($resp['code'] != 0){ //记录错误 return Model::asReturnError($resp['msg']); } } return Model::asReturnSuccess('ok',[]); $wxmpService = $plugin->getWxmpService(); $openid = $user->bindWxmp->openid; try{ $args = [ 'method' => 'gnete.upbc.code.trade', 'method_name' => 'create' ]; $remark = $paymentOrderUnion->order_no; $msgBody = []; $msgBody['merOrdrNo'] = $merOrderNo; //商户交易订单号,需保证在商户端不重复;格式:15 位商户号+8 位交易日期+9 位序数字列号 $msgBody['busiId'] = '00250007';//商户开通的业务 ID,00250007:小程序 $msgBody['bizFunc'] = '111011'; //业务功能,交易-111011 $msgBody['notifyUrl'] = $this->getNotifyUrl(SysConst::$cxPayTypeEnUnionpay); $msgBody['trxTtlAmt'] = $paymentOrderUnion->amount * 100;//交易总金额/订单总金额,单位分;【订单总金额】=【可打折金额】+【不可打折金额】 $msgBody['trxTtlAmt'] = (string)$msgBody['trxTtlAmt']; $subject = $paymentOrderUnion->title; $subject = $updcService->filterSpecialChar($subject); $msgBody['subject'] = $subject;//订单标题 $ordrDesc = $subject; $msgBody['ordrDesc'] = $ordrDesc;//订单描述 $msgBody['timeoutExpress'] = \Yii::$app->params['payTimeoutExpress'];//允许的最晚付款时间,逾期将关闭交易。取值范围:1m~24h。m-分钟,h-小时。 $msgBody['isMinipg'] = '1';//是否小程序支付。1-表示小程序支付;不传或值不为 1,表示公众账号内支付 /** * 交易类型 * JSAPI 微信公众号支付/微信小程序支付/支付宝服务窗/ * 云闪付支付 * NATIVE 扫码支付 * APP APP 支付(支持:微信 APP 支付) * MWEB H5 支付(支持:云闪付 H5、支付宝 H5) * APP-MINI APP 跳转小程序支付(支持:云闪付 APP、支 * 付宝 APP) * 商户 APP 支付场景使用交易类型说明:调用微信:APP, * 调用支付宝:APP-MINI,调用云闪付:APP-MINI。 */ $msgBody['trxType'] = 'JSAPI'; /** * 买家信息 */ $msgBody['buyerInf'] = [ //付款方银行代码,支持 3 位或 8 位银行代码,或金融机构编码;支付宝上送:00070707,微信上送:00060606,云闪付上送:00050505,公众号,小程序要必填 'bankCode' => '00060606', //用于微信支付,openId 为用户在主商户 appId 下的唯一标识(这里的 appId是我司的,openId 是用户静默授权给我司的)。openId 和 subOpenId 可以选传其中之一(商户一般会选择上送 subOpenId)。 //'openId' => '', //用于微信支付 subOpenId 为用户在主商户 subAppId 下的唯一标识(这里 的 subAppId 是商户的,具体参照下面 subAppId 字段的描 述。 subOpenId 是用户静默授权给商户的)。 openId 和 subOpenId 可以选传其中之一(商户一般会选择 上送 subOpenId),如果选择传 subOpenId,则必须同时传 subAppId。 'subOpenId' => $openid, //用于微信支付 如果是公众号支付,subAppId 对应商户的公众号的 appId; 如果是小程序支付,subAppId 对应商户小程序的 appId;; 如果是 APP 支付,subAppId 对应商户微信开放平台的应用appId; 'subAppId' => $wxmpService->appId, ]; $goodsDtl = []; foreach ($paymentOrderUnion->paymentOrder as $index => $paymentOrder){ $goodsDtlItem = []; $goodsDtlItem['gdsId'] = $paymentOrder->id; $goodsDtlItem['gdsNm'] = $paymentOrder->title; $goodsDtlItem['gdsCnt'] = 1; $goodsDtlItem['gdsPrice'] = $paymentOrder->amount * 100; $goodsDtlItem['gdsPrice'] = (string)$goodsDtlItem['gdsPrice']; $goodsDtl[] = $goodsDtlItem; } $msgBody['goodsDtl'] = $goodsDtl;//商品详情 $msgBody['remark'] = $remark; $biz_content = []; $biz_content['sndDt'] = date('YmdHis', time()); $biz_content['busiMerNo'] = $updcService->mchId; $biz_content['notifyUrl'] = $this->getNotifyUrl(SysConst::$cxPayTypeEnUnionpay); $biz_content['merOrderNo'] = $merOrderNo; $biz_content['msgBody'] = $msgBody; $biz_content['remark'] = $remark; \Yii::info('[UpdcRequest]'.json_encode($biz_content,JSON_UNESCAPED_UNICODE),'PaymentUpdc'); $biz_content = json_encode($biz_content); $args['biz_content'] = $biz_content; $res = $updcService->apiRequest($args); if(!isset($res['wcPayData']) || empty($res['wcPayData'])){ throw new \Exception('[60101]系统内部错误'); } $appPayData = $res['wcPayData']; } catch (\Exception $ex){ return Model::asReturnError($ex->getMessage()); } } else { return Model::asReturnError('账户尚未绑定微信,该订单不支持此付款方式'); } $data = array_merge([ 'pay_type' => $payType, 'id' => $paymentOrderUnion->id, ],$appPayData); } else { return $this->throwNotSupport($payType, $platform); } break; default: return Model::asReturnError('未知的`payType`。'); break; } return Model::asReturnSuccess('ok',$data); } private function throwNotSupport($pay_type, $platform) { $pay_type_cn = PaymentTypes::getPayType($pay_type); $platform_cn = SysConst::getPlatformByKey($platform); $msg = "{$platform_cn}暂不支持{$pay_type_cn}"; return Model::asReturnError($msg); } /** * @param $id * @return array */ public function payBuyBalance($id) { if (\Yii::$app->user->isGuest) { return Model::asReturnError('用户未登录。'); } $user = \Yii::$app->user->identity; $paymentOrderUnion = PaymentOrderUnion::findOne(['id' => $id, 'user_id' => $user->id]); if (!$paymentOrderUnion) { return Model::asReturnError('待支付订单不存在。'); } if (intval($paymentOrderUnion->is_pay) === 1) { return Model::asReturnError('订单已支付。'); } $supportPayTypes = (array)$paymentOrderUnion->decodeSupportPayTypes($paymentOrderUnion->support_pay_types); if (!empty($supportPayTypes) && is_array($supportPayTypes) && !in_array(SysConst::$cxPayTypeEnBalance, $supportPayTypes)) { if ($paymentOrderUnion->amount != 0) { // 订单金额为0时可以使用余额支付 return Model::asReturnError('暂不支持余额支付。'); } } $t = \Yii::$app->db->beginTransaction(); try { /** @var \app\models\PaymentOrder[] $paymentOrders */ $paymentOrders = \app\models\PaymentOrder::find() ->where(['payment_order_union_id' => $paymentOrderUnion->id,]) ->all(); $totalAmount = 0; foreach ($paymentOrders as $paymentOrder) { $totalAmount += $paymentOrder->amount; } $balanceAmount = $user->balance ? $user->balance->account_balance : 0.00; if ($balanceAmount < $totalAmount) { $t->rollBack(); return Model::asReturnError('账户余额不足。'); } $paymentOrderUnion->is_pay = 1; $paymentOrderUnion->pay_type = SysConst::$cxPayTypeBalance; if (!$paymentOrderUnion->save()) { $t->rollBack(); return Model::asReturnError($paymentOrderUnion->getFirstErrors()); } foreach ($paymentOrders as $paymentOrder) { $paymentOrder->is_pay = 1; $paymentOrder->pay_type = SysConst::$cxPayTypeBalance; if (!$paymentOrder->save()) { $t->rollBack(); return Model::asReturnError($paymentOrder->getFirstErrors()); } $po = new PaymentOrder([ 'orderNo' => $paymentOrder->order_no, 'amount' => (float)$paymentOrder->amount, 'title' => $paymentOrder->title, 'notifyClass' => $paymentOrder->notify_class, 'payType' => SysConst::$cxPayTypeEnBalance, ]); if ($po->amount > 0) { //账户余额变动 $order_type = UniqueOrderNo::getOrderTypeByOrderNo($po->orderNo); $ext = ""; $res = Balance::userWalletLog($user->id, Balance::TYPE_PAY, $po->amount, $po->title, $order_type, $po->orderNo, $ext, $paymentOrderUnion->cx_mch_id); if($res['code'] != SysErrCode::$apiReturnSuccess){ $t->rollBack(); return Model::asReturnError($res['msg']); } } $NotifyClass = $paymentOrder->notify_class; if(class_exists($NotifyClass)){ /** @var PaymentNotify $notifyObject */ $notifyObject = new $NotifyClass(); try { $resp = $notifyObject->notify($po); if($resp['code'] != 0){ $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$resp['msg']}"; \Yii::error($msg,'NotifyPayment'); } } catch (\Exception $exception) { $exp_err = $exception->getMessage(); $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$exp_err}"; \Yii::error($msg,'NotifyPayment'); } } else { //记录错误 $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$paymentOrder->notify_class}不存在"; \Yii::error($msg,'NotifyPayment'); } } $t->commit(); } catch (\Exception $e) { $t->rollBack(); return Model::asReturnError($e->getMessage()); } return Model::asReturnSuccess(); } /** * 积分支付 * @param $id * @return array */ public function payBuyIntegral($id) { if (\Yii::$app->user->isGuest) { return Model::asReturnError('用户未登录。'); } $user = \Yii::$app->user->identity; $paymentOrderUnion = PaymentOrderUnion::findOne(['id' => $id, 'user_id' => $user->id]); if (!$paymentOrderUnion) { return Model::asReturnError('待支付订单不存在。'); } if (intval($paymentOrderUnion->is_pay) === 1) { return Model::asReturnError('订单已支付。'); } $supportPayTypes = (array)$paymentOrderUnion->decodeSupportPayTypes($paymentOrderUnion->support_pay_types); if (!empty($supportPayTypes) && is_array($supportPayTypes) && !in_array(SysConst::$cxPayTypeEnIntegral, $supportPayTypes)) { if ($paymentOrderUnion->amount != 0) { // 订单金额为0时可以使用积分支付 return Model::asReturnError('暂不支持积分支付。'); } } $t = \Yii::$app->db->beginTransaction(); try { /** @var \app\models\PaymentOrder[] $paymentOrders */ $paymentOrders = \app\models\PaymentOrder::find() ->where(['payment_order_union_id' => $paymentOrderUnion->id,]) ->all(); $totalAmount = 0; $totalIntegral = 0; foreach ($paymentOrders as $paymentOrder) { $totalAmount += $paymentOrder->amount; $order = Order::findOne(['order_no' => $paymentOrder->order_no]); if($order == null){ $t->rollBack(); return Model::asReturnError('订单不存在'); } $totalIntegral += $order->integralMallOrder->integral_num; } $balanceAmount = $user->balance ? $user->balance->account_balance : 0.00; if ($balanceAmount < $totalAmount) { $t->rollBack(); return Model::asReturnError('账户余额不足。'); } $accountIntegral = $user->integral ? $user->integral->account_integral : 0; if($accountIntegral < $totalIntegral){ $t->rollBack(); return Model::asReturnError('账户积分不足。'); } $paymentOrderUnion->is_pay = 1; $paymentOrderUnion->pay_type = SysConst::$cxPayTypeIntegral; if (!$paymentOrderUnion->save()) { $t->rollBack(); return Model::asReturnError($paymentOrderUnion->getFirstErrors()); } foreach ($paymentOrders as $paymentOrder) { $paymentOrder->is_pay = 1; $paymentOrder->pay_type = SysConst::$cxPayTypeIntegral; if (!$paymentOrder->save()) { $t->rollBack(); return Model::asReturnError($paymentOrder->getFirstErrors()); } $po = new PaymentOrder([ 'orderNo' => $paymentOrder->order_no, 'amount' => (float)$paymentOrder->amount, 'title' => $paymentOrder->title, 'notifyClass' => $paymentOrder->notify_class, 'payType' => SysConst::$cxPayTypeEnBalance, ]); if ($po->amount > 0) { //账户余额变动 $order_type = UniqueOrderNo::getOrderTypeByOrderNo($po->orderNo); $ext = ""; $res = Balance::userWalletLog($user->id, Balance::TYPE_PAY, $po->amount, $po->title, $order_type, $po->orderNo, $ext, $paymentOrderUnion->cx_mch_id); if($res['code'] != SysErrCode::$apiReturnSuccess){ $t->rollBack(); return Model::asReturnError($res['msg']); } } $order = Order::findOne(['order_no' => $paymentOrder->order_no]); if($order->integralMallOrder->integral_num > 0){ //积分账户变动 $order_type = UniqueOrderNo::getOrderTypeByOrderNo($order->order_no); $ext = ""; $res = Integral::userIntegralWalletLog($user->id, Integral::TYPE_PAY, $order->integralMallOrder->integral_num, $po->title, $order_type, $order->order_no, $ext, $order->cx_mch_id); if($res['code'] != SysErrCode::$apiReturnSuccess){ $t->rollBack(); return Model::asReturnError($res['msg']); } } $NotifyClass = $paymentOrder->notify_class; if(class_exists($NotifyClass)){ /** @var PaymentNotify $notifyObject */ $notifyObject = new $NotifyClass(); try { $resp = $notifyObject->notify($po); if($resp['code'] != 0){ $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$resp['msg']}"; \Yii::error($msg,'NotifyPayment'); } } catch (\Exception $exception) { $exp_err = $exception->getMessage(); $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$exp_err}"; \Yii::error($msg,'NotifyPayment'); } } else { //记录错误 $msg = "[ORDER_NO:{$paymentOrder->order_no}] ERR_MSG:{$paymentOrder->notify_class}不存在"; \Yii::error($msg,'NotifyPayment'); } } $t->commit(); } catch (\Exception $e) { $t->rollBack(); return Model::asReturnError($e->getMessage()); } return Model::asReturnSuccess(); } private function getNotifyUrl($action) { $url = \Yii::$app->request->hostInfo . \Yii::$app->request->baseUrl . '/notify/payment/' . $action; return $url; } /** * @param PaymentTransfer $paymentTransfer * @return array */ public function transfer($paymentTransfer) { if (!$paymentTransfer instanceof PaymentTransfer) { return Model::asReturnError('无效的参数,参数不是有效的payment\\PaymentTransfer对象'); } $model = \app\models\PaymentTransfer::find() ->where([ 'transfer_order_no' => $paymentTransfer->order_no, 'user_id' => $paymentTransfer->user->id, 'cx_mch_id' => $paymentTransfer->cx_mch_id ])->one(); if($model == null){ $model = new \app\models\PaymentTransfer(); $model->cx_mch_id = $paymentTransfer->cx_mch_id; $model->user_id = $paymentTransfer->user->id; $model->order_no = UniqueOrderNo::generate(UniqueOrderNo::ORDER_TYPE_TRANSFER, "\\app\\models\\PaymentTransfer"); $model->transfer_order_no = $paymentTransfer->order_no; $model->amount = $paymentTransfer->amount; $model->pay_type = $paymentTransfer->transfer_type; $model->is_pay = 0; $model->title = $paymentTransfer->title; if(!$model->save()) return (new Model())->getModelError($model); } if($model->is_pay == 1){ return Model::asReturnError("该订单号已经打款,请勿重复操作"); } try{ $class = $this->transferClass($model->pay_type); if ($class->transfer($model, $paymentTransfer->user)) { return Model::asReturnSuccess(); } else { throw new PaymentException(); } } catch (PaymentException $ex){ return Model::asReturnError($ex->getMessage()); } } private function transferClass($type) { switch ($type){ case PaymentTransfer::TRANSFER_TYPE_WXPAY: $class = "app\\models\\common\\payment\\WxpayTransfer"; break; default: throw new PaymentException("无效的转账方式"); } if (!class_exists($class)) { throw new PaymentException("缺少{$class}对象"); } return new $class(); } }