| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- <?php
- namespace common\components\payment;
- use common\helpers\Url;
- use common\logic\wechat\WechatMessageLogic;
- use common\models\user\User;
- use EasyWeChat\Factory;
- use GuzzleHttp\Exception\GuzzleException;
- use http\Client;
- use Yii;
- use yii\helpers\ArrayHelper;
- use Omnipay\Omnipay;
- use common\enums\WechatPayTypeEnum;
- use WeChatPay\Builder;
- use WeChatPay\Crypto\Rsa;
- use WeChatPay\Util\PemUtil;
- use common\models\wechat\WechatTransferLog;
- /**
- * 微信支付类
- *
- * Class WechatPay
- * @package common\components\payment
- */
- class WechatPay
- {
- const DEFAULT = 'WechatPay';
- const APP = 'WechatPay_App';
- const NATIVE = 'WechatPay_Native';
- const JS = 'WechatPay_Js';
- const POS = 'WechatPay_Pos';
- const MWEB = 'WechatPay_Mweb';
- /**
- * 订单
- *
- * @var array
- */
- protected $order;
- /**
- * 配置
- *
- * @var
- */
- protected $config;
- /**
- * WechatPay constructor.
- */
- public function __construct($config)
- {
- $this->order = [
- 'spbill_create_ip' => Yii::$app->request->userIP??'',
- 'fee_type' => 'CNY',
- 'notify_url' => '',
- ];
- $this->config = $config;
- }
- /**
- * 实例化类
- *
- * @param $type
- * @return \Omnipay\WechatPay\AppGateway
- */
- private function create($type)
- {
- /* @var $gateway \Omnipay\WechatPay\AppGateway */
- $gateway = Omnipay::create($type);
- $gateway->setMchId($this->config['mch_id']);
- $gateway->setAppId($this->config['app_id']);
- $gateway->setApiKey($this->config['api_key']);
- $gateway->setCertPath($this->config['cert_client']);
- $gateway->setKeyPath($this->config['cert_key']);
- return $gateway;
- }
- /**
- * 回调
- *
- * @return \Omnipay\WechatPay\Message\CompletePurchaseResponse
- */
- public function notify()
- {
- $gateway = $this->create(self::DEFAULT);
- return $gateway->completePurchase([
- 'request_params' => file_get_contents('php://input')
- ])->send();
- }
- /**
- * 微信APP支付网关
- * @param array $order
- * [
- * 'body' => 'The test order',
- * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
- * 'total_fee' => 1, //=0.01
- * ]
- * @param bool $debug
- * @return mixed
- */
- public function app($order, $debug = false)
- {
- $gateway = $this->create(self::APP);
- $gateway->setAppId($this->config['open_app_id']);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- return $debug ? $response->getData() : $response->getAppOrderData();
- }
- /**
- * 微信原生扫码支付支付网关
- *
- * @param array $order
- * [
- * 'body' => 'The test order',
- * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
- * 'total_fee' => 1, //=0.01
- * ]
- * @param bool $debug
- * @return mixed
- */
- public function native($order, $debug = false)
- {
- $gateway = $this->create(self::NATIVE);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- return $debug ? $response->getData() : $response->getCodeUrl();
- }
- /**
- * 微信js支付支付网关
- *
- * @param array $order
- * [
- * 'body' => 'The test order',
- * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
- * 'total_fee' => 1, //=0.01
- * 'openid' => 'ojPztwJ5bRWRt_Ipg', //=0.01
- * ]
- * @param bool $debug
- * @return mixed
- */
- public function js($order, $debug = false)
- {
- $gateway = $this->create(self::JS);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- // 兼容EasyWechat
- Yii::$app->params['wechatPaymentConfig'] = [
- 'app_id' => $this->config['app_id'],
- 'mch_id' => $this->config['mch_id'],
- 'key' => $this->config['api_key'],
- 'cert_path' => Yii::getAlias($this->config['cert_client']),
- 'key_path' => Yii::getAlias($this->config['cert_key']),
- ];
- $data = $response->getJsOrderData();
- if (isset($data['timeStamp'])) {
- $data['timestamp'] = $data['timeStamp'];
- unset($data['timeStamp']);
- }
- return $debug ? $response->getData() : $data;
- }
- /**
- * @param $order
- * @param bool $debug
- * @return array|mixed|null
- */
- public function miniProgram($order, $debug = false)
- {
- $gateway = $this->create(self::JS);
- $gateway->setAppId($this->config['mini_program_app_id']);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- $data = $response->getJsOrderData();
- if (isset($data['timeStamp'])) {
- $data['timestamp'] = $data['timeStamp'];
- unset($data['timeStamp']);
- }
- return $debug ? $response->getData() : $data;
- }
- /**
- * 微信刷卡支付网关
- *
- * @param array $order
- * [
- * 'body' => 'The test order',
- * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
- * 'total_fee' => 1, //=0.01,
- * 'auth_code' => '',
- * ]
- * @param bool $debug
- * @return mixed
- */
- public function pos($order, $debug = false)
- {
- $gateway = $this->create(self::POS);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- return $debug ? $response->getData() : $response->getData();
- }
- /**
- * 微信H5支付网关
- * @param array $order
- * [
- * 'body' => 'The test order',
- * 'out_trade_no' => date('YmdHis') . mt_rand(1000, 9999),
- * 'total_fee' => 1, //=0.01
- * ]
- * @param bool $debug
- * @return mixed
- */
- public function mweb($order, $debug = false)
- {
- $gateway = $this->create(self::MWEB);
- $request = $gateway->purchase(ArrayHelper::merge($this->order, $order));
- $response = $request->send();
- return $debug ? $response->getData() : $response->getData();
- }
- /**
- * 关闭订单
- *
- * @param $out_trade_no
- * @return
- */
- public function close($out_trade_no)
- {
- $gateway = $this->create(self::DEFAULT);
- $response = $gateway->close([
- 'out_trade_no' => $out_trade_no, //The merchant trade no
- ])->send();
- return $response->getData();
- }
- /**
- * 查询订单
- *
- * @param $transaction_id
- * @return
- */
- public function query($transaction_id)
- {
- $gateway = $this->create(self::DEFAULT);
- $response = $gateway->query([
- 'transaction_id' => $transaction_id, //The wechat trade no
- ])->send();
- return $response->getData();
- }
- public function queryByOutTradeNo($out_trade_no)
- {
- $gateway = $this->create(self::DEFAULT);
- $response = $gateway->query([
- 'out_trade_no' => $out_trade_no, //The wechat trade no
- ])->send();
- return $response->getData();
- }
- public function queryRefund($out_trade_no){
- $gateway = $this->create(self::DEFAULT);
- $response = $gateway->queryRefund([
- 'out_trade_no' => $out_trade_no, //The wechat trade no
- ])->send();
- return $response->getData();
- }
- /**
- * 退款
- *
- * 订单类型
- *
- * @param $info
- * [
- * 'transaction_id' => $transaction_id, //The wechat trade no
- * 'out_refund_no' => $outRefundNo,
- * 'total_fee' => 1, //=0.01
- * 'refund_fee' => 1, //=0.01
- * ]
- * @param $type
- * @return
- */
- public function refund($info, $type = WechatPayTypeEnum::JS)
- {
- $gateway = $this->create(self::DEFAULT);
- switch ($type) {
- case WechatPayTypeEnum::JS :
- //小程序支付回调 trade_type = JSAPI,这里得判断相关app_id 是否设置
- if(empty($this->config['app_id'])){
- $app_id = $this->config['mini_program_app_id'] ?: $this->config['open_app_id'];
- $gateway->setAppId($app_id);
- }
- break;
- case WechatPayTypeEnum::MINI_PROGRAM :
- //小程序
- $gateway->setAppId($this->config['mini_program_app_id']);
- break;
- case WechatPayTypeEnum::APP :
- //app
- $gateway->setAppId($this->config['open_app_id']);
- break;
- }
- $response = $gateway->refund($info)->send();
- return $response->getData();
- }
- /**
- * User: wniu
- * Date: 2021/11/1
- * Time: 3:19 下午
- * *
- * @param $paymentTransfer
- * @param $userInfo
- * @return bool
- * @throws \Exception
- */
- public function transfer($paymentTransfer, $userInfo)
- {
- $t = \Yii::$app->db->beginTransaction();
- try {
- $config = Yii::$app->services->pay->getWechatConfig($paymentTransfer->mall_id);
- $wechatConfig = [
- 'app_id' => $config['app_id'],
- 'mch_id' => $config['wechat_mch_id'],
- 'key' => $config['wechat_pay_secret'], // API 密钥
- 'cert_path' => dirname(Yii::$app->basePath).'/'.$config['wechat_cert_pem_file'],
- 'key_path' => dirname(Yii::$app->basePath).'/'.$config['wechat_key_pem_file'],
- 'notify_url' => ''//回调地址
- ];
- if($userInfo->platform == User::PLATFORM_MP_WX) {
- $mini_program = \Yii::$app->services->setting->mall->get($paymentTransfer->mall_id, 'mini_program');
- $wechatConfig['app_id'] =$mini_program['app_id'];
- }
- $payment = Factory::payment($wechatConfig);
- //打款操作调整到保存成功后再调用打款,避免保存出错,款项依然打出去了,造成重复打款的问题
- $paymentTransfer->is_pay = 1;
- if (!$paymentTransfer->save()) {
- throw new \Exception(json_encode($paymentTransfer->errors));
- }else{
- //todo 需要在微信开通该产品权限
- $result = $payment->transfer->toBalance([
- 'partner_trade_no' => $paymentTransfer->order_no, // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
- 'openid' => $userInfo->openid,
- 'check_name' => 'NO_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
- 're_user_name' => '', // 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名
- 'amount' => $paymentTransfer->amount * 100, // 企业付款金额,单位为分
- 'desc' => '收益提现', // 企业付款操作说明信息。必填
- ]);
- if(isset($result["return_code"]) && $result["return_code"] == "SUCCESS" && isset($result["result_code"]) && $result["result_code"] == "SUCCESS"){
- }else{
- $msg = isset($result["return_msg"]) ? $result["return_msg"] : "提现失败";
- $msg = isset($result["err_code_des"]) ? $result["err_code_des"] : $msg;
- throw new \Exception($msg);
- }
- }
- $t->commit();
- return true;
- }
- catch (\Exception $e) {
- $t->rollBack();
- throw new \Exception($e->getMessage());
- }
- }
- /**
- * @desc:退款
- * @Author: hua
- * @Date: 2021/12/20
- * @Time: 15:34
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $payment_refund
- * @param $pay_total_fee
- * @return bool
- * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
- */
- public function refunds($payment_refund,$pay_total_fee){
- $config = Yii::$app->services->pay->getWechatConfig($payment_refund->mall_id);
- $wechatConfig = [
- 'app_id' => $config['app_id'],
- 'mch_id' => $config['wechat_mch_id'],
- 'key' => $config['wechat_pay_secret'], // API 密钥
- 'cert_path' => dirname(Yii::$app->basePath).'/'.$config['wechat_cert_pem_file'],
- 'key_path' => dirname(Yii::$app->basePath).'/'.$config['wechat_key_pem_file'],
- 'notify_url' => ''//回调地址
- ];
- $payment = Factory::payment($wechatConfig);
- $res = $payment->refund->byOutTradeNumber($payment_refund->out_trade_no, $payment_refund->order_no, $pay_total_fee*100, $payment_refund->amount*100);
- if(isset($res["return_code"]) && $res["return_code"] == "SUCCESS" && isset($res["result_code"]) && $res["result_code"] == "SUCCESS"){
- $payment_refund->is_pay = 1;
- $payment_refund->save();
- return true;
- }else{
- throw new \Exception($res['err_code_des']);
- }
- }
- /**
- * 商家转账到零钱
- * @param \common\models\common\PaymentTransfer $paymentTransfer
- * @param $userInfo
- * @throws \Exception
- * @return bool
- */
- public function v3_transfer_batches($paymentTransfer, $userInfo)
- {
- $cash_info = $paymentTransfer->getCashInfo();
- $mall_id = $paymentTransfer->mall_id;
- $config = Yii::$app->services->pay->getWechatConfig($paymentTransfer->mall_id);
- if($userInfo->platform == User::PLATFORM_MP_WX) {
- $mini_program = \Yii::$app->services->setting->mall->get($paymentTransfer->mall_id, 'mini_program');
- $config['app_id'] =$mini_program['app_id'];
- }
- $instance = $this->getV3Instance($mall_id,$config);
- $paymentTransfer->is_pay = 1;
- if (!$paymentTransfer->save()) {
- throw new \Exception($paymentTransfer->getErrorMessage());
- }
- $total_amount = bcmul($paymentTransfer->amount,100);
- $total_amount = intval($total_amount);
- $batch_name = date('Y').'年'.date('m').'月收益提现';
- $data = [
- 'out_batch_no' => $paymentTransfer->order_no, //商家批次单号
- 'appid' => $config['app_id'], //申请商户号的appid或商户号绑定的appid
- 'batch_name' => $batch_name, //批次名称
- 'batch_remark' => $batch_name, //批次备注
- 'total_amount' => $total_amount , //转账总金额 ,int 类型,转账金额单位为“分”
- 'total_num' => 1 , //转账总笔数
- 'transfer_detail_list' => [
- [
- 'out_detail_no' => $paymentTransfer->order_no.'1', //商家明细单号
- 'transfer_amount' => intval(bcmul($paymentTransfer->amount,100)), //转账金额
- 'transfer_remark' => '收益提现', //转账备注
- 'openid' => $userInfo->openid,
- //'user_name' => '' //非必要
- ]
- ],
- ];
- //以下是打款的零钱的操作
- try {
- $resp = $instance
- ->chain('v3/transfer/batches')
- ->post(['json' => $data]);
- $result = $resp->getBody();
- $transfer_result = '';
- if($resp->getStatusCode() == 200 && $result){
- $result = json_decode($result,true);
- $transfer_result = json_encode($result);
- }
- \Yii::error('wechat transfer getStatusCode='.$resp->getStatusCode().' getBody'.$resp->getBody());
- \Yii::error('insert WechatTransfer data:'.$paymentTransfer->transfer_order_no.','.json_encode($data).','.$resp->getBody().','.$mall_id.','.$cash_info->id);
- /** @var WechatTransferLog $transfer_model */
- $transfer_model = new WechatTransferLog();
- $transfer_model->out_trade_no = $paymentTransfer->order_no;
- $transfer_model->transfer_data = json_encode($data);
- $transfer_model->transfer_result = $transfer_result;
- $transfer_model->mall_id = $mall_id;
- $transfer_model->cash_id = $cash_info->id;
- $res = $transfer_model->save();
- if(!$res){
- Yii::error('wechat transfer log result save error:'.$transfer_model->getErrorMessage());
- }
- return true;
- } catch (\Exception $e) {
- \Yii::error('wechat transfer exception='.$e->getMessage());
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- \Yii::error('wechat transfer exception getStatusCode='.$r->getStatusCode() . ' ' . $r->getReasonPhrase());
- \Yii::error('wechat transfer exception getBody='.$r->getBody());
- $body = json_decode($r->getBody(),true);
- throw new \Exception($body['message']??'');
- }
- return false;
- }
- }
- public function v3_transfer_bills($paymentTransfer, $userInfo)
- {
- $cash_info = $paymentTransfer->getCashInfo();
- $mall_id = $paymentTransfer->mall_id;
- $config = Yii::$app->services->pay->getWechatConfig($paymentTransfer->mall_id);
- if ($userInfo->platform == User::PLATFORM_MP_WX) {
- $mini_program = \Yii::$app->services->setting->mall->get($paymentTransfer->mall_id, 'mini_program');
- $config['app_id'] = $mini_program['app_id'];
- }
- $paymentTransfer->is_pay = 1;
- if (!$paymentTransfer->save()) {
- throw new \Exception($paymentTransfer->getErrorMessage());
- }
- $total_amount = bcmul($paymentTransfer->amount, 100);
- $total_amount = intval($total_amount);
- $batch_name = date('Y') . '年' . date('m') . '月收益提现';
- // 获取当前微信支付 apiv3 秘钥
- $wechatSetting = Yii::$app->services->setting->mall->get($paymentTransfer->mall_id, 'pay.wechat');
- if ($wechatSetting) $wechatSetting = json_decode($wechatSetting, true);
- $wechat_pay_secret_v3 = $wechatSetting['wechat_pay_secret_v3'] ?? '';
- $data = [
- 'appid' => $config['app_id'], //申请商户号的appid或商户号绑定的appid
- 'out_bill_no' => $paymentTransfer->order_no, //商家批次单号
- "transfer_scene_id" => "1000",
- 'openid' => $userInfo->openid,
- 'transfer_amount' => $total_amount, //转账总金额 ,int 类型,转账金额单位为“分”
- 'transfer_remark' => $batch_name, //批次备注
- 'notify_url' => Url::toApi(["notify/wechat-transfer/{$wechat_pay_secret_v3}"], TRUE), // 回调地址
- "user_recv_perception" => "现金奖励",
- 'transfer_scene_report_infos' => [
- [
- "info_type" => "活动名称",
- "info_content" => "提现"
- ],
- [
- 'info_type' => '奖励说明', //转账备注
- 'info_content' => '收益提现', //转账备注
- ]
- ],
- ];
- $body = json_encode($data, JSON_UNESCAPED_UNICODE);
- $host = 'https://api.mch.weixin.qq.com';
- $uri = '/v3/fund-app/mch-transfer/transfer-bills';
- // 请求头
- $headers = $this->getHeaders($mall_id, $config, $data, $uri);
- //以下是打款的零钱的操作
- try {
- $client = new \GuzzleHttp\Client();
- $resp = $client->request('POST', $host . $uri, [
- 'headers' => $headers,
- 'body' => $body
- ]);
- $result = $resp->getBody();
- $result = json_decode($result, true);
- if ($resp->getStatusCode() == 200 && $result) {
- $transfer_result = json_encode($result);
- } else {
- throw new \Exception($body['message'] ?? '');
- }
- \Yii::error('wechat transfer getStatusCode=' . $resp->getStatusCode() . ' getBody' . $resp->getBody());
- /** @var WechatTransferLog $transfer_model */
- $transfer_model = new WechatTransferLog();
- $transfer_model->out_trade_no = $paymentTransfer->order_no;
- $transfer_model->transfer_data = json_encode($data);
- $transfer_model->transfer_result = $transfer_result;
- $transfer_model->mall_id = $mall_id;
- $transfer_model->cash_id = $cash_info->id;
- $res = $transfer_model->save();
- if (!$res) {
- Yii::error('wechat transfer log result save error:' . $transfer_model->getErrorMessage());
- }
- return $result;
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- $body = json_decode($r->getBody(), true);
- throw new \Exception($body['message'] ?? '');
- }
- return false;
- }
- }
- protected function getHeaders($mall_id, $config, $data, $uri)
- {
- $timestamp = time();
- $nonceStr = uniqid();
- $body = json_encode($data, JSON_UNESCAPED_UNICODE);
- $message = "POST\n{$uri}\n" . $timestamp . "\n" . $nonceStr . "\n" . $body . "\n";
- if(empty($data)){
- $message = "GET\n{$uri}\n" . $timestamp . "\n" . $nonceStr . "\n". "\n";
- }
- $privateKey = $config['wechat_key_pem'];
- openssl_sign($message, $rawSignature, $privateKey, 'SHA256');
- $signature = base64_encode($rawSignature);
- $merchantId = $config['wechat_mch_id'];
- $wechat_serial = $config['wechat_serial'];
- //微信支付平台证书
- $platformCertificateFilePath = dirname(Yii::$app->basePath) . '/backend/runtime/pem/' . $mall_id . '/wechatpay.pem';
- if (!file_exists($platformCertificateFilePath)) {
- //证书生成方式: 更新vendor包后,在项目更目录执行 composer exec CertificateDownloader.php -- -m 你的商户号 -s 40字节你的商户证书序列号 -f 你的私钥文件路径 -k 你的APIv3密钥 -o /www/wwwroot/qimall/backend/runtime/pem/1/
- //生成后名称如下:/www/wwwroot/qimall/backend/runtime/pem/1/wechatpay_703A2B6B830559BB4D59BE21EC7694F4909E57D2.pem
- //生成后需要 修改文件名称 : wechatpay.pem,如:wechatpay.pem
- $platformCertificateSerial = $config['wechat_pay_public_key_id'];
- } else {
- $platformCertificateFilePath = 'file://' . $platformCertificateFilePath;
- // 从「微信支付平台证书」中获取「证书序列号」
- $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
- }
- return [
- 'Accept' => 'application/json',
- 'Content-Type' => 'application/json',
- 'Authorization' => 'WECHATPAY2-SHA256-RSA2048 mchid="' . $merchantId . '",nonce_str="' . $nonceStr . '",timestamp="' . $timestamp . '",serial_no="' . $wechat_serial . '",signature="' . $signature . '"',
- 'Wechatpay-Serial' => $platformCertificateSerial,
- ];
- }
- /**
- * 获取微信V3支付初始化类
- * @param int $mall_id
- * @param array $config 商家微信配置
- * @return mixed
- * @throws \Exception
- */
- private function getV3Instance($mall_id,$config)
- {
- //商户号
- $merchantId = $config['wechat_mch_id'];
- // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
- $merchantPrivateKeyFilePath = 'file://'.dirname(Yii::$app->basePath).'/'.$config['wechat_key_pem_file'];
- $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
- // 「商户API证书」的「证书序列号」
- $merchantCertificateSerial = $config['wechat_serial'];
- //微信支付平台证书
- $platformCertificateFilePath = dirname(Yii::$app->basePath).'/backend/runtime/pem/'.$mall_id.'/wechatpay.pem';
- if(!file_exists($platformCertificateFilePath)){
- //证书生成方式: 更新vendor包后,在项目更目录执行 composer exec CertificateDownloader.php -- -m 你的商户号 -s 40字节你的商户证书序列号 -f 你的私钥文件路径 -k 你的APIv3密钥 -o /www/wwwroot/qimall/backend/runtime/pem/1/
- //生成后名称如下:/www/wwwroot/qimall/backend/runtime/pem/1/wechatpay_703A2B6B830559BB4D59BE21EC7694F4909E57D2.pem
- //生成后需要 修改文件名称 : wechatpay.pem,如:wechatpay.pem
- throw new \Exception('微信支付平台证书不存在');
- }
- $platformCertificateFilePath = 'file://'.$platformCertificateFilePath;
- $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
- // 从「微信支付平台证书」中获取「证书序列号」
- $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
- // 构造一个 APIv3 客户端实例
- $instance = Builder::factory([
- 'mchid' => $merchantId,
- 'serial' => $merchantCertificateSerial,
- 'privateKey' => $merchantPrivateKeyInstance,
- 'certs' => [
- $platformCertificateSerial => $platformPublicKeyInstance,
- ],
- ]);
- return $instance;
- }
- /**
- * 查询转账批次单以及指定状态的转账明细单
- * @param WechatTransferLog $wechat_transfer_log
- * @return bool
- * @throws \Exception
- */
- public function queryBatches(&$wechat_transfer_log)
- {
- $mall_id = $wechat_transfer_log->mall_id;
- $config = Yii::$app->services->pay->getWechatConfig($mall_id);
- $instance = $this->getV3Instance($mall_id,$config);
- $wechat_transfer = json_decode($wechat_transfer_log->transfer_result,true);
- $batch_id = $wechat_transfer['batch_id'];
- //以下是打款的零钱的操作
- try {
- //微信批次单号查询批次单API
- $resp = $instance
- ->v3->transfer->batches->batchId->_batch_id_->getAsync([
- // Query 参数
- 'query' => ['need_query_detail' => true,'offset' => 0,'limit'=>100,'detail_status'=>'ALL'],
- // 变量名 => 变量值
- 'batch_id' => $batch_id,
- ])->then(static function($response) {
- // 正常逻辑回调处理
- return $response;
- })->wait();
- $batches = json_decode($resp->getBody()->getContents(), true);
- if(!$batches){
- return [];
- }
- //微信批次状态
- $batch_status = $batches['transfer_batch']['batch_status'] ?? '';
- $wechat_transfer_log->batch_status = $batch_status;
- //明细状态
- $detail_status = '';
- $transfer_detail_list = $batches['transfer_detail_list'] ?? [];
- $detail_info = [];
- if($batches && $transfer_detail_list){
- foreach($batches['transfer_detail_list'] as $detail){
- $detail_status = $detail['detail_status'] ?? '';
- //微信明细单号查询明细单API
- if($detail_status == 'FAIL'){
- //如果明细单号失败,查询 微信明细单号查询明细单API
- $resp = $instance
- ->v3->transfer->batches->batchId->_batch_id_->details->detailId->_detail_id_->getAsync([
- // Query 参数
- // 变量名 => 变量值
- 'batch_id' => $batch_id,
- 'detail_id' => $detail['detail_id']
- ])->then(static function($response) {
- // 正常逻辑回调处理
- return $response;
- })->wait();
- $detail_info = json_decode($resp->getBody()->getContents(), true);
- $wechat_transfer_log->transfer_detail = json_encode($detail_info);
- }
- }
- }
- if($detail_status){
- $wechat_transfer_log->detail_status = $detail_status;
- }
- if($wechat_transfer_log->save() === false){
- \Yii::error('wechat transfer save error='.$wechat_transfer_log->getErrorMessage());
- }
- return $batches;
- } catch (\Exception $e) {
- \Yii::error('wechat getBatchesInfo exception='.$e->getMessage());
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- \Yii::error('wechat getBatchesInfo exception getStatusCode='.$r->getStatusCode() . ' ' . $r->getReasonPhrase());
- \Yii::error('wechat getBatchesInfo exception getBody='.$r->getBody());
- }
- return false;
- }
- }
- /**
- * out_trade_no/out_bill_no
- * @param $out_bill_no
- * @return bool|string
- * @throws GuzzleException
- */
- public function mchTransfer($out_bill_no){
- try {
- $wechatTransferLog = \common\models\wechat\WechatTransferLog::find()->where(['out_trade_no' => $out_bill_no])->one();
- if (empty($wechatTransferLog)) throw new \Exception($out_bill_no.'记录不存在');
- $transferResult = json_decode($wechatTransferLog->transfer_result,true);
- $config = Yii::$app->services->pay->getWechatConfig($wechatTransferLog->mall_id);
- $mini_program = \Yii::$app->services->setting->mall->get($wechatTransferLog->mall_id, 'mini_program');
- $config['app_id'] = $mini_program['app_id'];
- $host = 'https://api.mch.weixin.qq.com';
- $uri = '/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/'.$out_bill_no;
- // 请求头
- $headers = $this->getHeaders($wechatTransferLog->mall_id, $config, [], $uri);
- //以下是打款的零钱的操作
- $client = new \GuzzleHttp\Client();
- $resp = $client->request('get', $host . $uri, [
- 'headers' => $headers,
- ]);
- $result = $resp->getBody();
- $result = json_decode($result, true);
- if(isset($result['code']) && !isset($result['state'])){
- throw new \Exception($result['code']);
- }
- $transferResult['state'] = $result['state'];
- $wechatTransferLog->transfer_result = json_encode($transferResult);
- $res = $wechatTransferLog->save();
- if(!$res){
- throw new \Exception($out_bill_no.'数据更新失败');
- }
- return true;
- }catch (\Exception $e){
- return $e->getMessage();
- }
- }
- }
|