diff --git a/app/Model/v3/CcbPayment.php b/app/Model/v3/CcbPayment.php index 9e19e2a..9538726 100644 --- a/app/Model/v3/CcbPayment.php +++ b/app/Model/v3/CcbPayment.php @@ -1,35 +1,36 @@ 'integer', 'order_main_id' => 'integer', 'ordr_tamt' => 'float', 'txn_tamt' => 'float', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + protected $casts = [ + 'id' => 'integer', + 'order_main_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + public function orderMain() + { + return $this->belongsTo(OrderMain::class, 'order_main_id', 'global_order_id'); + } } \ No newline at end of file diff --git a/app/Service/v3/CcbPaymentService.php b/app/Service/v3/CcbPaymentService.php index 5bddf33..2f35e78 100644 --- a/app/Service/v3/CcbPaymentService.php +++ b/app/Service/v3/CcbPaymentService.php @@ -2,13 +2,17 @@ namespace App\Service\v3; +use App\Commons\Log; use App\Constants\v3\ErrorCode; use App\Constants\v3\LogLabel; use App\Constants\v3\OrderState; use App\Exception\BusinessException; use App\Exception\ErrorCodeException; +use App\Model\v3\CcbPayment; use App\Model\v3\OrderMain; +use App\Model\v3\Store; use App\Model\v3\User; +use Hyperf\Di\Annotation\Inject; use Hyperf\Guzzle\ClientFactory; use Hyperf\Logger\LoggerFactory; @@ -38,6 +42,18 @@ class CcbPaymentService */ private $mktId; + /** + * 我方商家编号 + * @var string + */ + private $merchantId; + + /** + * 支付方式 + * @var string + */ + private $pymdCd; + /** * 我方私钥 * @var resource @@ -72,6 +88,12 @@ class CcbPaymentService */ private $logger; + /** + * @Inject + * @var Log + */ + protected $log; + public function __construct(ClientFactory $clientFactory, LoggerFactory $loggerFactory) { $this->clientFactory = $clientFactory; @@ -79,6 +101,8 @@ class CcbPaymentService $this->isDebug = config('ccb.debug'); $this->mktId = config('ccb.mkt_id'); + $this->merchantId = config('ccb.merchant_id'); + $this->pymdCd = config('ccb.pymd_cd'); $selfPrivateKey = config('ccb.self_private_key'); $selfPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split($selfPrivateKey, 64, "\n")."-----END RSA PRIVATE KEY-----\n"; @@ -269,18 +293,22 @@ class CcbPaymentService return date('YmdHis').mt_rand(10000, 99999).mt_rand(10000, 99999); } + private function genMainOrderNo() + { + return time().mt_rand(10000, 99999).mt_rand(10000, 99999); + } + /** * 3.1 生成支付订单接口 * @param string $Main_Ordr_No 主订单编号 * @param string $Ordr_Tamt 订单总金额 * @param string $Txn_Tamt 交易总金额(实付) - * @param string $Pymd_Cd 支付方式:03为H5,05为小程序 - * @param string $Sub_Appid 当前调起支付的小程序appid - * @param string $Sub_Openid 用户在小程序下的openid + * @param string|null $Sub_Appid 当前调起支付的小程序appid + * @param string|null $Sub_Openid 用户在小程序下的openid * @param array $Orderlist 子订单列表 * @return array */ - public function gatherPlaceorder(string $Main_Ordr_No, string $Ordr_Tamt, string $Txn_Tamt, string $Pymd_Cd, string $Sub_Appid, string $Sub_Openid, array $Orderlist) + public function gatherPlaceorder(string $Main_Ordr_No, string $Ordr_Tamt, string $Txn_Tamt, ?string $Sub_Appid, ?string $Sub_Openid, array $Orderlist) { $uri = '/online/direct/gatherPlaceorder'; @@ -291,19 +319,22 @@ class CcbPaymentService 'Ittparty_Jrnl_No' => $this->genSerialNumber(), 'Mkt_Id' => $this->mktId, 'Main_Ordr_No' => $Main_Ordr_No, - 'Pymd_Cd' => $Pymd_Cd, + 'Pymd_Cd' => $this->pymdCd, 'Py_Ordr_Tpcd' => '04', 'Py_Rslt_Ntc_Sn' => '1', 'Ccy' => '156', 'Ordr_Tamt' => $Ordr_Tamt, 'Txn_Tamt' => $Txn_Tamt, - 'Sub_Appid' => $Sub_Appid, - 'Sub_Openid' => $Sub_Openid, 'Order_Time_Out' => '1800', 'Orderlist' => $Orderlist, 'Vno' => '4', ]; + if ($this->pymdCd == '05') { + $params['Sub_Appid'] = $Sub_Appid; + $params['Sub_Openid'] = $Sub_Openid; + } + return $this->apiRequest($uri, $params); } @@ -313,11 +344,12 @@ class CcbPaymentService * @param string $Cmdty_Ordr_No 子订单编号 * @param string $Ordr_Amt 订单金额 * @param string $Txnamt 实付金额 + * @param string $Cmdty_Dsc 商品描述 * @return string[] */ - public function subOrderListItem(string $Mkt_Mrch_Id, string $Cmdty_Ordr_No, string $Ordr_Amt, string $Txnamt) + public function subOrderListItem(string $Mkt_Mrch_Id, string $Cmdty_Ordr_No, string $Ordr_Amt, string $Txnamt, string $Cmdty_Dsc) { - return compact('Mkt_Mrch_Id', 'Cmdty_Ordr_No', 'Ordr_Amt', 'Txnamt'); + return compact('Mkt_Mrch_Id', 'Cmdty_Ordr_No', 'Ordr_Amt', 'Txnamt', 'Cmdty_Dsc'); } /** @@ -457,4 +489,140 @@ class CcbPaymentService return $this->apiRequest($uri, $params); } + + /** + * 创建支付订单 + * @param $globalOrderId + * @param $userId + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function createCcbPayment($globalOrderId, $userId) + { + try { + // 待支付的,未超时(15min,900sec)的订单 + $orderMain = OrderMain::query() + ->where(['state' => OrderState::UNPAID, 'global_order_id' => $globalOrderId, 'user_id' => $userId]) + ->where('created_at', '>=', (time() - 900)) + ->first(); + if (empty($orderMain)) { + throw new ErrorCodeException(ErrorCode::ORDER_NOT_AVAILABLE, '[支付订单号]'.$globalOrderId); + } + + $user = User::select('openid')->find($userId); + + $model = new CcbPayment(); + $model->order_main_id = $orderMain->global_order_id; + $model->main_ordr_no = $this->genMainOrderNo(); + $model->pymd_cd = $this->pymdCd; + if ($this->pymdCd == '05') { + $model->sub_appid = config('wechat.applet.app_id'); + $model->sub_openid = $user->openid; + } + $model->py_ordr_tpcd = '04'; + $model->ordr_tamt = $orderMain->money; + $model->txn_tamt = $orderMain->money; + $subOrderList = []; + + // 运费判断 + if (bccomp($orderMain->delivery_money, '0', 2) == 1) { + $subOrderList[] = $this->subOrderListItem( + $this->merchantId, + $model->main_ordr_no.'D', + $orderMain->delivery_money, + $orderMain->delivery_money, + '配送费' + ); + } + + // 我方分润比例% + $selfProfitRatio = config('ccb.self_profit_ratio'); + + if (bccomp($selfProfitRatio, '0', 3) == 0) { + throw new BusinessException(500, '未配置分润比例,请联系管理员'); + } + + foreach ($orderMain->orders as $order) { + if (empty($order->store->ccb_merchant_id)) { + throw new BusinessException(500, '店铺'.$order->store_id.'未配置商户编号,请联系管理员'); + } + foreach ($order->orderGoods as $orderGoods) { + $goodsMoney = bcmul($orderGoods->price, (string)$orderGoods->number, 2); + $selfProfitMoney = bcmul($goodsMoney, $selfProfitRatio, 2); + $merchantProfitMoney = bcsub($goodsMoney, $selfProfitMoney, 2); + // 平台抽佣后最小金额不能为0 + if (bccomp($merchantProfitMoney, '0', 2) == 0) { + $selfProfitMoney = '0.00'; + $merchantProfitMoney = $goodsMoney; + } + $subOrderList[] = $this->subOrderListItem( + $order->store->ccb_merchant_id, + $model->main_ordr_no.'G'.$orderGoods->id.'N01', + $merchantProfitMoney, + $merchantProfitMoney, + $orderGoods->name.'(分佣到账)' + ); + if (bccomp($selfProfitMoney, '0', 2) == 1) { + $subOrderList[] = $this->subOrderListItem( + $this->merchantId, + $model->main_ordr_no.'G'.$orderGoods->id.'N02', + $selfProfitMoney, + $selfProfitMoney, + $orderGoods->name.'(平台抽佣)' + ); + } + } + } + + $model->orderlist = json_encode($subOrderList); + $model->save(); + + $result = $this->gatherPlaceorder( + $model->main_ordr_no, + $model->ordr_tamt, + $model->txn_tamt, + $model->sub_appid, + $model->sub_openid, + $subOrderList + ); + + $model->py_trn_no = $result['Py_Trn_No']; + $model->prim_ordr_no = $result['Prim_Ordr_No']; + $model->ordr_gen_tm = $result['Ittparty_Tms']; + if (isset($result['Cshdk_Url'])) { + $model->cshdk_url = $result['Cshdk_Url']; + } + if ($this->pymdCd == '05') { + $model->rtn_par_data = json_encode($result['Rtn_Par_Data']); + } + $model->ordr_stcd = $result['Ordr_Stcd']; + $model->rtn_orderlist = json_encode($result['Orderlist']); + $model->save(); + + if ($this->pymdCd == '05') { + // 返回支付参数给前端 + $parameters = [ + 'appId' => $result['Rtn_Par_Data']['appId'], + 'timeStamp' => $result['Rtn_Par_Data']['timeStamp'], + 'nonceStr' => $result['Rtn_Par_Data']['nonceStr'], + 'package' => $result['Rtn_Par_Data']['package'], + 'signType' => $result['Rtn_Par_Data']['signType'], + 'paySign' => $result['Rtn_Par_Data']['paySign'], + ]; + } else { + $parameters = [ + 'Cshdk_Url' => $model->cshdk_url, + ]; + } + + $parameters['order_main_id'] = $orderMain->global_order_id; + + return $parameters; + + } catch (\Exception $e) { + $this->log->event(LogLabel::ORDER_PAYMENT_LOG, ['payment_do_exception_msg' => $e->getMessage()]); + throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, '[稍后重试]'); + } + } + } \ No newline at end of file diff --git a/test/Cases/CCBTest.php b/test/Cases/CCBTest.php index dd9442a..7ad242b 100644 --- a/test/Cases/CCBTest.php +++ b/test/Cases/CCBTest.php @@ -73,21 +73,27 @@ class CCBTest extends HttpTestCase // var_export($result); // } - public function testQuery() - { - $ccb = ApplicationContext::getContainer()->get(CcbPaymentService::class); - - $result = $ccb->gatherEnquireOrder('m2020060915410278990'); - - var_export($result); - } +// public function testQuery() +// { +// $ccb = ApplicationContext::getContainer()->get(CcbPaymentService::class); +// +// $result = $ccb->gatherEnquireOrder('m2020060915410278990'); +// +// var_export($result); +// } +// +// public function testOrderInfQuery() +// { +// $ccb = ApplicationContext::getContainer()->get(CcbPaymentService::class); +// +// $result = $ccb->orderInfQuery('m2020060915410278990', '10500000780012708212236595958H'); +// +// var_export($result); +// } - public function testOrderInfQuery() + public function testCreateCcbPayment() { $ccb = ApplicationContext::getContainer()->get(CcbPaymentService::class); - - $result = $ccb->orderInfQuery('m2020060915410278990', '10500000780012708212236595958H'); - - var_export($result); + $ccb->createCcbPayment('415142026151948289', 193155); } } \ No newline at end of file