123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- <?php
- namespace core\pay;
- use app\dict\common\ChannelDict;
- use app\dict\pay\OnlinePayDict;
- use app\dict\pay\RefundDict;
- use app\dict\pay\TransferDict;
- use core\exception\PayException;
- use Psr\Http\Message\MessageInterface;
- use Psr\Http\Message\ResponseInterface;
- use think\facade\Log;
- use think\Response;
- use Throwable;
- use Yansongda\Artful\Exception\InvalidResponseException;
- use Yansongda\Pay\Exception\ContainerException;
- use Yansongda\Pay\Exception\InvalidParamsException;
- use Yansongda\Pay\Exception\ServiceNotFoundException;
- use Yansongda\Pay\Pay;
- use Yansongda\Pay\Plugin\Wechat\V3\Marketing\MchTransfer\CancelPlugin;
- use Yansongda\Supports\Collection;
- /**
- * 微信支付管理驱动类 todo 注意:暂时不考虑合单类业务
- * Class FileDriver
- * @package core\file
- */
- class Wechatpay extends BasePay
- {
- /**
- * @param array $config
- * @return void
- * @throws ContainerException
- */
- protected function initialize(array $config = [])
- {
- $this->config = $config;
- $config['mch_secret_cert'] = url_to_path($config['mch_secret_cert'] ?? '');
- $config['mch_public_cert_path'] = url_to_path($config['mch_public_cert_path'] ?? '');
- // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
- $config['mode'] = Pay::MODE_NORMAL;
- if (!empty($config['wechat_public_cert_path']) && !empty($config['wechat_public_cert_id'])) {
- $config['wechat_public_cert_path'] = [
- $config['wechat_public_cert_id'] => url_to_path($config['wechat_public_cert_path'])
- ];
- } else {
- unset($config['wechat_public_cert_path']);
- unset($config['wechat_public_cert_id']);
- }
- Pay::config($this->payConfig($config, 'wechat'));
- }
- /**
- * 公众号支付
- * @param array $params
- * @return mixed|Collection
- */
- public function mp(array $params)
- {
- try {
- $result = $this->returnFormat(Pay::wechat()->mp([
- 'out_trade_no' => $params['out_trade_no'],
- 'description' => $params['body'],
- 'amount' => [
- 'total' => $params['money'],
- ],
- 'payer' => [
- 'openid' => $params['openid'],
- ],
- ]));
- $code = $result['code'] ?? 0;
- if ($code == 0) return $result;
- //支付错误抛出
- throw new PayException($result['message']);
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 手机网页支付
- * @param array $params
- * @return mixed
- */
- public function wap(array $params)
- {
- try {
- $order = [
- 'out_trade_no' => $params['out_trade_no'],
- 'description' => $params['body'],
- 'amount' => [
- 'total' => $params['money'],
- ],
- 'scene_info' => [
- 'payer_client_ip' => request()->ip(),
- 'h5_info' => [
- 'type' => 'Wap',
- ]
- ],
- ];
- //这儿有些特殊, 默认情况下,H5 支付所使用的 appid 是微信公众号的 appid,即配置文件中的 mp_app_id 参数,如果想使用关联的小程序的 appid,则只需要在调用参数中增加 ['_type' => 'mini'] 即可
- if (!empty($order['type'])) {
- $order['_type'] = 'mini'; // 注意这一行
- }
- return $this->returnFormat(Pay::wechat()->h5($order));
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- public function web(array $params)
- {
- }
- /**
- * app支付
- * @param array $params
- * @return mixed|ResponseInterface
- */
- public function app(array $params)
- {
- try {
- return $this->returnFormat(Pay::wechat()->app([
- 'out_trade_no' => $params['out_trade_no'],
- 'description' => $params['body'],
- 'amount' => [
- 'total' => $params['money'],
- ],
- ]));
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 小程序支付
- * @param array $params
- * @return mixed|ResponseInterface
- */
- public function mini(array $params)
- {
- try {
- return $this->returnFormat(Pay::wechat()->mini([
- 'out_trade_no' => $params['out_trade_no'],
- 'description' => $params['body'],
- 'amount' => [
- 'total' => $params['money'],
- 'currency' => 'CNY',//一般是人民币
- ],
- 'payer' => [
- 'openid' => $params['openid'],
- ]
- ]));
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 付款码支付
- * @param array $params
- * @return mixed|Collection
- */
- public function pos(array $params)
- {
- try {
- $order = [
- 'out_trade_no' => $params['out_trade_no'],
- 'body' => $params['body'],
- 'total_fee' => $params['money'],
- 'spbill_create_ip' => request()->ip(),
- 'auth_code' => $params["auth_code"],
- ];
- $result = Pay::wechat()->pos($order);
- return $this->returnFormat($result);
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 扫码支付
- * @param array $params
- * @return mixed|Collection
- */
- public function scan(array $params)
- {
- try {
- return $this->returnFormat(Pay::wechat()->scan([
- 'out_trade_no' => $params['out_trade_no'],
- 'description' => $params['body'],
- 'amount' => [
- 'total' => $params['money'],
- ],
- ]));
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 转账(微信的转账是很多笔的)
- * @param array $params
- * @return array
- */
- public function transfer(array $params)
- {
- $to_data = $params['to_no'];//收款人数据
- $channel = $to_data['channel'] ?? '';//渠道
- $open_id = $to_data['open_id'] ?? '';//openid
- $transfer_scene_id = (string)$to_data['transfer_scene_id'] ?? '';//openid
- $transfer_scene_report_infos = $to_data['transfer_scene_report_infos'] ?? [];//openid
- $user_recv_perception = $to_data['user_recv_perception'] ?? '';//openid
- if(empty($this->config['mch_id']) || empty($this->config['mch_secret_key']) || empty($this->config['mch_secret_cert']) || empty($this->config['mch_public_cert_path'])){
- throw new PayException('WECHAT_TRANSFER_CONFIG_NOT_EXIST');
- }
- if(empty($transfer_scene_id)){
- throw new PayException('PLEASE_CONTACT_THE_ADMINISTRATOR_TO_CONFIGURE_THE_WECHAT_TRANSFER_SCENARIO_ID');//请联系管理员配置微信转账场景ID');
- }
- $transfer_no = $params['transfer_no'] ?? '';
- $remark = $params['remark'] ?? '';
- $order = [
- '_action' => 'mch_transfer', // 微信官方老版本下线后,此部分可省略
- 'out_bill_no' => $transfer_no,
- 'transfer_scene_id' => $transfer_scene_id,
- 'openid' => $open_id,
- // 'user_name' => '闫嵩达' // 明文传参即可,sdk 会自动加密
- 'transfer_amount' => (int)$params['money'],
- 'transfer_remark' => $remark,
- 'transfer_scene_report_infos' =>$transfer_scene_report_infos,
- 'notify_url' => $this->config['notify_url'],
- 'user_recv_perception' => $user_recv_perception
- ];
- if($channel == ChannelDict::WEAPP){
- $order['_type'] = 'mini';
- }
- $tran_status_list = [
- 'PROCESSING' => TransferDict::DEALING,
- 'ACCEPTED' => TransferDict::DEALING,
- 'WAIT_USER_CONFIRM' => TransferDict::WAIT_USER,//等待收款用户确认
- 'TRANSFERING' => TransferDict::WAIT_USER_ING,//转账中
- 'FAIL' => TransferDict::FAIL,
- 'SUCCESS' => TransferDict::SUCCESS,
- 'CANCELING' => TransferDict::FAIL_ING,
- 'CANCELLED' => TransferDict::FAIL,
- ];
- try {
- $result = $this->returnFormat(Pay::wechat()->transfer($order));
- if (!empty($result['code'])) {
- // if($result['code'] == 'PARAM_ERROR'){
- // throw new PayException();
- // }else if($result['code'] == 'INVALID_REQUEST'){
- // throw new PayException();
- // }
- if ($result['code'] == 'INVALID_REQUEST') {
- throw new PayException(700010);
- }
- throw new PayException($result['message']);
- }
- $result['mch_id'] = $this->config['mch_id'];
- $result['appid'] = $channel == ChannelDict::WEAPP ? $this->config['mini_app_id'] : $this->config['mp_app_id'];
- return ['out_bill_no' => $result['out_bill_no'], 'transfer_bill_no' => $result['transfer_bill_no'], 'status' => $tran_status_list[$result['state']], 'reason' => $result['fail_reason'] ?? '', 'package_info' => $result['package_info'] ?? [], 'extra' => $result];
- } catch (\Exception $e) {
- // if($e->getCode() == 9402){
- // return ['batch_id' => '', 'out_batch_no' => $order['out_batch_no'], 'status' => TransferDict::DEALING];
- // }
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- /**
- * 支付关闭
- * @param string $out_trade_no
- * @return void
- * @throws ContainerException
- * @throws InvalidParamsException
- * @throws ServiceNotFoundException
- */
- public function close(string $out_trade_no)
- {
- try {
- $result = Pay::wechat()->close([
- 'out_trade_no' => $out_trade_no,
- ]);
- return $this->returnFormat($result);
- }catch(Throwable $e){
- return false;
- }
- return true;
- }
- /**
- * 退款
- * @param array $params
- * @return array
- * @throws ContainerException
- * @throws InvalidParamsException
- * @throws ServiceNotFoundException
- */
- public function refund(array $params)
- {
- $out_trade_no = $params['out_trade_no'];
- $money = $params['money'];
- $total = $params['total'];
- $refund_no = $params['refund_no'];
- $result = Pay::wechat()->refund([
- 'out_trade_no' => $out_trade_no,
- 'out_refund_no' => $refund_no,
- 'amount' => [
- 'refund' => $money,
- 'total' => $total,
- 'currency' => 'CNY',
- ],
- ]);
- $result = $this->returnFormat($result);
- $refund_status_array = [
- 'SUCCESS' => RefundDict::SUCCESS,
- 'CLOSED' => RefundDict::FAIL,
- 'PROCESSING' => RefundDict::DEALING,
- 'ABNORMAL' => RefundDict::FAIL,
- ];
- return [
- 'status' => $refund_status_array[$result['status']],
- 'refund_no' => $refund_no,
- 'out_trade_no' => $out_trade_no,
- 'pay_refund_no' => $result['refund_id']
- ];
- }
- /**
- * 异步回调
- * @param string $action
- * @param callable $callback
- * @return ResponseInterface|Response
- */
- public function notify(string $action, callable $callback)
- {
- try {
- Log::write('wechat_start'.$action);
- $result = $this->returnFormat(Pay::wechat()->callback());
- Log::write('wechat_start_1');
- Log::write($result);
- Log::write('wechat_start_1');
- if ($action == 'pay') {//支付
- if ($result['event_type'] == 'TRANSACTION.SUCCESS') {
- $pay_trade_data = $result['resource']['ciphertext'];
- $temp_params = [
- 'trade_no' => $pay_trade_data['transaction_id'],
- 'mch_id' => $pay_trade_data['mchid'],
- 'status' => OnlinePayDict::getWechatPayStatus($pay_trade_data['trade_state'])
- ];
- $callback_result = $callback($pay_trade_data['out_trade_no'], $temp_params);
- if (is_bool($callback_result) && $callback_result) {
- return Pay::wechat()->success();
- }
- }
- } else if ($action == 'refund') {//退款
- if ($result['event_type'] == 'REFUND.SUCCESS') {
- $refund_trade_data = $result['resource']['ciphertext'];
- $refund_status_array = [
- 'SUCCESS' => RefundDict::SUCCESS,
- 'CLOSED' => RefundDict::FAIL,
- 'PROCESSING' => RefundDict::DEALING,
- 'ABNORMAL' => RefundDict::FAIL,
- ];
- $temp_params = [
- 'trade_no' => $refund_trade_data['transaction_id'],
- 'mch_id' => $refund_trade_data['mchid'],
- 'refund_no' => $refund_trade_data['out_refund_no'],
- 'status' => $refund_status_array[$refund_trade_data['refund_status']],
- ];
- $callback_result = $callback($refund_trade_data['out_trade_no'], $temp_params);
- if (is_bool($callback_result) && $callback_result) {
- return Pay::wechat()->success();
- }
- }
- }else if ($action == 'transfer') {//转账
- if ($result['event_type'] == 'MCHTRANSFER.BILL.FINISHED') {
- $refund_trade_data = $result['resource']['ciphertext'];
- $tran_status_list = [
- 'PROCESSING' => TransferDict::DEALING,
- 'ACCEPTED' => TransferDict::DEALING,
- 'WAIT_USER_CONFIRM' => TransferDict::WAIT_USER,//等待收款用户确认
- 'TRANSFERING' => TransferDict::WAIT_USER_ING,//转账中
- 'FAIL' => TransferDict::FAIL,
- 'SUCCESS' => TransferDict::SUCCESS,
- 'CANCELING' => TransferDict::FAIL_ING,
- 'CANCELLED' => TransferDict::FAIL,
- ];
- $temp_params = [
- // 'out_bill_no' => $refund_trade_data['out_bill_no'],
- 'mch_id' => $refund_trade_data['mch_id'],
- 'out_bill_no' => $refund_trade_data['out_bill_no'] ?? '',
- 'transfer_bill_no' => $refund_trade_data['transfer_bill_no'] ?? '',
- 'status' => $tran_status_list[$refund_trade_data['state']],
- 'reason' => $refund_trade_data['fail_reason'] ?? '',
- ];
- Log::write('wechat_start_2');
- Log::write($temp_params);
- Log::write('wechat_start_2');
- $callback_result = $callback($refund_trade_data['out_bill_no'], $temp_params);
- if (is_bool($callback_result) && $callback_result) {
- return Pay::wechat()->success();
- }
- }
- }
- return $this->fail();
- } catch ( Throwable $e ) {
- // throw new PayException($e->getMessage());
- Log::write('wechat_error'.$e->getMessage().$e->getLine().$e->getFile());
- return $this->fail($e->getMessage());
- }
- }
- /**
- * 查询普通支付订单
- * @param array $params
- * @return array|MessageInterface|Collection|null
- * @throws ContainerException
- * @throws InvalidParamsException
- * @throws ServiceNotFoundException
- */
- public function getOrder(array $params = [])
- {
- $out_trade_no = $params['out_trade_no'];
- $transaction_id = $params['transaction_id'] ?? '';
- $order = [
- ];
- if (!empty($out_trade_no)) {
- $order['out_trade_no'] = $out_trade_no;
- }
- if (!empty($transaction_id)) {
- $order['transaction_id'] = $transaction_id;
- }
- $result = Pay::wechat()->query($order);
- if (empty($result))
- return $result;
- $result = $this->returnFormat($result);
- return [
- 'status' => OnlinePayDict::getWechatPayStatus($result['trade_state']),
- ];
- }
- /**
- * 查询退款单据
- * @param string|null $out_trade_no
- * @param string|null $refund_no
- * @return array|Collection|MessageInterface|null
- * @throws ContainerException
- * @throws InvalidParamsException
- * @throws ServiceNotFoundException
- */
- public function getRefund(?string $out_trade_no, ?string $refund_no = '')
- {
- $order = [
- '_action' => 'refund',
- 'transaction_id' => $out_trade_no,
- 'out_refund_no' => $refund_no,
- ''
- ];
- $result = Pay::wechat()->query($order);
- if (empty($result))
- return $result;
- $result = $this->returnFormat($result);
- $refund_status_array = [
- 'SUCCESS' => RefundDict::SUCCESS,
- 'CLOSED' => RefundDict::FAIL,
- 'PROCESSING' => RefundDict::DEALING,
- 'ABNORMAL' => RefundDict::FAIL,
- ];
- return [
- 'status' => $refund_status_array[$result['status']],
- 'refund_no' => $refund_no,
- 'out_trade_no' => $out_trade_no
- ];
- }
- /**
- * 获取转账订单(todo 切勿调用)
- * @param string $transfer_no
- * @return array
- * @throws ContainerException
- * @throws InvalidParamsException
- */
- public function getTransfer(string $transfer_no, $out_batch_no = '')
- {
- $order = [
- 'out_bill_no' => $transfer_no,
- 'transfer_bill_no' => $out_batch_no,
- '_action' => 'transfer',
- ];
- $tran_status_list = [
- 'PROCESSING' => TransferDict::DEALING,
- 'ACCEPTED' => TransferDict::DEALING,
- 'WAIT_USER_CONFIRM' => TransferDict::WAIT_USER,//等待收款用户确认
- 'TRANSFERING' => TransferDict::WAIT_USER_ING,//转账中
- 'FAIL' => TransferDict::FAIL,
- 'SUCCESS' => TransferDict::SUCCESS,
- 'CANCELING' => TransferDict::FAIL_ING,
- 'CANCELLED' => TransferDict::FAIL,
- ];
- try {
- $result = Pay::wechat()->query($order);
- $result = $this->returnFormat($result);
- //微信转账状态
- // $transfer_status_array = [
- // 'INIT' => TransferDict::DEALING,//初始态。 系统转账校验中
- // 'WAIT_PAY' => TransferDict::DEALING,
- // 'PROCESSING' => TransferDict::DEALING,
- // 'FAIL' => TransferDict::FAIL,
- // 'SUCCESS' => TransferDict::SUCCESS,
- // ];
- return [
- 'status' => $tran_status_list[$result['state']],
- 'transfer_no' => $transfer_no,
- 'reason' => $result['fail_reason'] ?? '',
- 'out_bill_no' => $result['out_bill_no'] ?? '',
- 'transfer_bill_no' => $result['transfer_bill_no'] ?? ''
- ];
- }catch(Throwable $e){
- return [
- 'status' => TransferDict::DEALING,
- 'transfer_no' => $transfer_no,
- 'reason' => $e->getMessage(),
- ];
- }
- }
- /**
- * 转账取消
- * @param array $params
- * @return array
- */
- public function transferCancel(array $params)
- {
- if(empty($this->config['mch_id']) || empty($this->config['mch_secret_key']) || empty($this->config['mch_secret_cert']) || empty($this->config['mch_public_cert_path'])){
- throw new PayException('WECHAT_TRANSFER_CONFIG_NOT_EXIST');
- }
- $transfer_no = $params['transfer_no'] ?? '';
- $tran_status_list = [
- 'CANCELING' => TransferDict::FAIL_ING,
- 'CANCELLED' => TransferDict::FAIL,
- ];
- $order = [
- '_action' => 'cancel', // 微信官方老版本下线后,此部分可省略
- 'out_bill_no' => $transfer_no,
- 'notify_url' => $this->config['notify_url'],
- ];
- try {
- $allPlugins = Pay::wechat()->mergeCommonPlugins([CancelPlugin::class]);
- $result = Pay::wechat()->pay($allPlugins, $order);
- // $result = $this->returnFormat(Pay::wechat()->transfer($order));
- if (!empty($result['code'])) {
- //
- if ($result['code'] == 'INVALID_REQUEST') {
- throw new PayException(700010);
- }
- throw new PayException($result['message']);
- }
- return ['out_bill_no' => $result['out_bill_no'], 'transfer_bill_no' => $result['transfer_bill_no'], 'status' => $tran_status_list[$result['state']]];
- } catch (\Exception $e) {
- if ($e instanceof InvalidResponseException) {
- throw new PayException($e->response->all()['message'] ?? '');
- }
- throw new PayException($e->getMessage());
- }
- }
- public function fail($message = '')
- {
- $response = [
- 'code' => 'FAIL',
- 'message' => $message ?: '失败',
- ];
- return response($response, 400, [], 'json');
- }
- }
|