首页
关于
统计
友联
留言
壁纸
Search
1
微信支付电子发票接入介绍
84 阅读
2
宝塔 设置 SSH 通过密钥登录
83 阅读
3
laravel 使用当中碰到的问题
75 阅读
4
laravel 关于 options请求的处理
62 阅读
5
linux关闭ssh密钥登录(禁止免密登录)
56 阅读
默认分类
后端积累
前端积累
日记
数据库
项目
登录
Search
阳
累计撰写
38
篇文章
累计收到
222
条评论
首页
栏目
默认分类
后端积累
前端积累
日记
数据库
项目
页面
关于
统计
友联
留言
壁纸
搜索到
38
篇与
yang
的结果
2021-07-05
FTP上传文件失败,提示553 Can't open that file: Permission denied
FTP文件上传失败,提示553 Can't open that file: Permission denied解决方法:目录的所属组,所属用户属于root,所以导致FTP无法上传, 修改组和所属用户为www即可。chown -fR www ./* chgrp -fR www ./*
2021年07月05日
35 阅读
0 评论
0 点赞
2021-07-01
fatal: remote origin already exists. (远程来源已经存在 解决办法)
在当我们输入 git remote add origin https://gitee.com/(github/码云账号)/(github/码云项目名).git 就会报如下的错fatal: remote origin already exists. 翻译过来就是:致命:远程来源已经存在此时,我们可以先 git remote -v 查看远程库信息:可以看到,本地库已经关联了origin的远程库,并且,该远程库指向GitHub。解决办法如下:1、先输入$ git remote rm origin (删除关联的origin的远程库)2、再输入$ git remote add origin git@github.com:(github名)/(git项目名).git 就不会报错了!3、如果输入$ git remote rm origin 还是报错的话,error: Could not remove config section 'remote.origin'. 我们需要修改gitconfig文件的内容
2021年07月01日
27 阅读
0 评论
0 点赞
2021-07-01
货运APP项目正式启动
市场是公平的,付出总有回报。前提是不局限于当下,敢于尝试。2021年7月1日 上午10点简单开了会 启动了货运的项目 两个月之后成品出来就开始专做软件服务商
2021年07月01日
33 阅读
0 评论
0 点赞
2021-07-01
laravel 使用composer 安装出现的问题
首先更改镜像composer config -g repo.packagist composer https://packagist.org
2021年07月01日
32 阅读
0 评论
0 点赞
2021-06-30
imagick处理pdf文件
碰到项目需要将pdf转为图片 或者 把pdf切分为多张图片宝塔找到对应的php版本,安装imagickfunction pdf2png($pdf,$path,$page=-1) { if(!extension_loaded('imagick')) { return false; } if(!file_exists($pdf)) { return false; } if(!is_readable($pdf)) { return false; } $im = new \Imagick(); $im->setResolution(150,150); $im->setCompressionQuality(100); if($page==-1) $im->readImage($pdf); else $im->readImage($pdf."[".$page."]"); $i=0; foreach ($im as $Key => $Var) { $Var->setImageFormat('png'); $filename = $path. 'page_'.$i.'.png'; if($Var->writeImage($filename) == true) { $Return[] = $filename; } $i++; } //返回转化图片数组,由于pdf可能多页,此处返回二维数组。 return $Return; }当出现这样的错误 ,需要安装ghostscript扩展 Uncaught exception 'ImagickException' with message 'Failed to read the file'yum install -y ghostscript
2021年06月30日
18 阅读
0 评论
0 点赞
2021-06-21
微信电子发票--“自建平台模式”--小程序开票源码
项目说明我有一个小程序,是交通行业的。用户买完票之后,可以在电子票页面申请开电子发票;小程序开发票用的是公众号的access_token,发送到用户卡包也是通过公众号发的,小程序要做的是申请电子发票和完成授权。虽然买完票就能申请发票,但是为了减少后续的冲红操作,在用户乘车之后才会去申请第三方平台开发票;代码用的是PHP。项目大概逻辑商户获取获取access_token。(方法:getAccessToken())提前获取开票平台标识s_pappid(方法:getInvoiceUrl())设置商户联系方式(方法:setContact())商户获取授权页(方法:getTicket()); 商户获取授权页url(方法:getAuthUrl());根据第5步的结果,在小程序开票按钮的点击事件上部署跳转到小程序授权页的逻辑;获取用户填写的抬头信息有两种办法:1)主动查询授权状态(方法:getAuthData())、2)根据授权回调获取授权状态(方法:getAuthDataWxAccount());创建发票卡券模板(方法:getInvoiceCardId());在自建发票平台开具电子发票(意思是,自己去开发票,比如通过第三方开票平台,然后把开票返回的发票号码、发票代码、发票pdf文件等保存下来;最后再用这些信息调用微信的PDF上传接口和下发到用户卡包的接口);上传发票PDF文件(方法:setPDF());将电子发票添加到用户微信卡包(方法:sendInvoice())。代码与说明 说明:域名空间下面引用的类,基本上都是数据库操作,大家可以不用看,主要是所取到的要用的字段含义。AppIdModel用的表是zc2_ticket_appid,请求微信接口用到的字段基本都在这张表了: CREATE TABLE `zc2_ticket_appid` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(50) NOT NULL COMMENT '调用方名称', `desc` varchar(100) NOT NULL COMMENT '调用方描述', `appid` varchar(50) NOT NULL COMMENT '调用方APPID', `appsecret` varchar(50) DEFAULT NULL COMMENT '调用方秘钥', `wx_appid` varchar(50) NOT NULL DEFAULT '' COMMENT '微信APPID', `wx_appsecret` varchar(125) NOT NULL DEFAULT '' COMMENT '微信APPSECRET', `wx_mch_id` varchar(50) NOT NULL DEFAULT '' COMMENT '商户ID,多商户小程序在zc2_sale_address表配置', `pay_acid` int(11) NOT NULL DEFAULT '0' COMMENT '支付商户ID,多商户小程序在zc2_sale_address表配置', `access_token` varchar(1000) NOT NULL DEFAULT '' COMMENT '小程序access_token', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1:有效 2:无效', `createtime` int(10) DEFAULT '0', `createuser` int(10) DEFAULT '0', `updatetime` int(10) DEFAULT '0', `updateuser` int(10) DEFAULT '0', `bonus_insurance` tinyint(1) NOT NULL DEFAULT '2' COMMENT '赠送保险开关:1 开,2 关', `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '渠道类别:1前端;2管理后台;3设备接口', `auth_ticket` varchar(1000) NOT NULL DEFAULT '' COMMENT '授权页ticket凭证', `invoice_url` varchar(512) NOT NULL DEFAULT '' COMMENT '开票平台链接', `invoice_card_id` varchar(512) NOT NULL DEFAULT '' COMMENT '发票卡券模板', `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '商户联系手机', `wx_account_appid` varchar(50) NOT NULL DEFAULT '' COMMENT '小程序关联的微信公众号APPID', `wx_account_appsecret` varchar(125) NOT NULL DEFAULT '' COMMENT '小程序关联的微信公众号APPSECRET', `wx_account_access_token` varchar(1000) NOT NULL DEFAULT '' COMMENT '小程序关联的微信公众号access_token', PRIMARY KEY (`id`), UNIQUE KEY `appid` (`appid`) ) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='接口调用方appid表'; <?php /** * 电子发票 */ namespace Kgkx\Domain; use Focusspot\Model\AppId AS AppIDModel; use Focusspot\Common\Exception\LocalServiceException; use PhalApi\CUrl; use Focusspot\Common\Exception\RemoteServiceException; use Kgkx\Model\OrderTicket AS ModelOrderTickets; use Kgkx\Model\Invoice AS ModelInvoice; use Kgkx\Model\AppConfig AS ModelAppConfig; class Invoice { public function __construct() { } /** * @Function: 获取AccessToken * @param $appId * @return mixed * @throws LocalServiceException * @throws RemoteServiceException * 从数据库查询access_token 如果过期,则调用微信官方接口,获取新的 * access_token,并更新新的access_token和过期时间到数据库 */ public function getAccessToken($appId) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row) { //存在数据库的公众号access_token $token = empty($row['wx_account_access_token']) ? null : json_decode($row['wx_account_access_token'], true); if($token && $token['expire'] > time()) { return $token['access_token']; }else { $wxAccountAppid = $row['wx_account_appid'];//公众号appid $wxAccountAppSecret = $row['wx_account_appsecret'];//公众号appsecret $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$wxAccountAppid}&secret={$wxAccountAppSecret}"; $curl = new CUrl(30); $ret = $curl->get($url); $ret = json_decode($ret, true); if(isset($ret['errcode'])) { \PhalApi\DI()->logger->error('Function getAccessToken.$url : ' . $url); throw new RemoteServiceException("获取AccessToken失败:".$ret['errmsg'],98); }else { //保存到数据库 $expire = time() + $ret['expires_in'] - 200; //过期时间 $accessToken = $ret['access_token']; $updateData = array('wx_account_access_token' => json_encode(array('access_token' => $accessToken, 'expire' => $expire))); $params = array('wx_appid' => $appId); $appIdModel->updateData($updateData, $params); return $accessToken; } } }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @function 验证access_token是否有效 * @param $access_token * @return bool */ public function checkAccessToken($access_token) { \PhalApi\DI()->logger->info('Function checkAccessToken.$access_token : ' . $access_token); if(!empty($access_token)) { $url = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=".$access_token; $curl = new CUrl(30); $ret = $curl->get($url); \PhalApi\DI()->logger->info('Function checkAccessToken.$ret : ' . $ret); $ret = json_decode($ret, true); if(!isset($ret['errcode'])) { return true; } } return false; } /** * @function 获取自身的开票平台识别码 * @param $appId * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException * @desc 开票平台可以通过此接口获得本开票平台的预开票url,进而获取s_pappid。 * 开票平台将该s_pappid并透传给商户,商户可以通过该s_pappid参数在微信 * 电子发票方案中标识出为自身提供开票服务的开票平台。 */ public function getInvoiceUrl($appId) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row) { $invoiceUrl = empty($row['invoice_url']) ? null : $row['invoice_url']; if($invoiceUrl) { return $invoiceUrl; }else { $token = $this->getAccessToken($appId); $url = "https://api.weixin.qq.com/card/invoice/seturl?access_token={$token}"; $curl = new CUrl(30); $curlRes = $curl->post($url,json_encode(array())); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$res : ' . json_encode($curlRes)); if($ret['errcode']==0 and $ret['errmsg']=='ok') { $invoiceUrl = $ret['invoice_url']; $updateData = array('invoice_url' => $invoiceUrl); $params = array('wx_appid' => $appId); $appIdModel->updateData($updateData, $params); return $invoiceUrl; }else { throw new RemoteServiceException("获取自身的开票平台识别码失败:".$ret['errcode'].$ret['errmsg'],98); } } }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @function 设置商户联系方式 * @param $appId * @param $time_out * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException * @desc 商户获取授权链接之前,需要先设置商户的联系方式 */ public function setContact($appId,$time_out) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row and $row['phone']) { //1.获取AccessToken $token = $this->getAccessToken($appId); //2. $phone = $row['phone']; $url = "https://api.weixin.qq.com/card/invoice/setbizattr?action=set_contact&access_token={$token}"; $params = json_encode(array('contact'=>array('phone'=>$phone,'time_out'=>$time_out))); $curl = new CUrl(30); $curlRes = $curl->post($url,$params); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$res : ' . json_encode($curlRes)); if(!empty($ret['errcode'])) { throw new RemoteServiceException("设置商户联系方式失败:".$ret['errcode'].$ret['errmsg'],98); }else { return $ret; } }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @function 获取授权页ticket * @param $appId * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException * @desc 商户在调用授权页前需要先获取一个7200s过期的授权页ticket,在获取授权页接口中, * 该ticket作为参数传入,加强安全性。 */ public function getTicket($appId) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row) { $authTicket = empty($row['auth_ticket']) ? null : json_decode($row['auth_ticket'], true); if($authTicket && $authTicket['expire'] > time()) { return $authTicket['ticket']; }else { $token = $this->getAccessToken($appId); $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$token}&type=wx_card"; $curl = new CUrl(30); $ret = $curl->get($url); $ret = json_decode($ret, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$ret : ' . json_encode($ret)); if(!empty($ret['errcode'])) { throw new RemoteServiceException("获取网页授权ticket失败:".$ret['errcode'].$ret['errmsg'],98); }else { $expire = time() + $ret['expires_in'] - 200; //过期时间 $ticket = $ret['ticket']; $updateData = array('auth_ticket' => json_encode(array('ticket' => $ticket, 'expire' => $expire))); $params = array('wx_appid' => $appId); $appIdModel->updateData($updateData, $params); return $ticket; } } }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @function 获取授权页链接 * @param $appId * @param $s_pappid string 开票平台在微信的标识号,商户需要找开票平台提供 * @param $order_id string 订单id,在商户内单笔开票请求的唯一识别号 * @param $money string 订单金额,以分为单位 * @param $timestamp int 时间戳 * @param $source string 开票来源,app:app开票,web:微信h5开票,wxa:小程序开发票,wap:普通网页开票 * @param $redirect_url string 授权成功后跳转页面。本字段只有在source为H5的时候需要填写,引导用户在微信中进行下一步流程。app开票因为从外部app拉起微信授权页,授权完成后自动回到原来的app,故无需填写。 * @param $ticket string 授权页ticket * @param $type int 授权类型,0:开票授权,1:填写字段开票授权,2:领票授权 * * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException */ public function getAuthUrl($appId,$s_pappid,$order_id,$money,$timestamp,$source,$redirect_url,$ticket,$type) { //1.获取AccessToken $token = $this->getAccessToken($appId); //2. $url = "https://api.weixin.qq.com/card/invoice/getauthurl?access_token={$token}"; $params = array( 's_pappid'=>$s_pappid, 'order_id'=>$order_id, 'money'=>$money, 'timestamp'=>$timestamp, 'source'=>$source, 'redirect_url'=>$redirect_url, 'ticket'=>$ticket, 'type'=>$type, ); $curl = new CUrl(30); $curlRes = $curl->post($url,json_encode($params)); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$res : ' . json_encode($curlRes)); if(!empty($ret['errcode'])) { throw new RemoteServiceException("获取网页授权url失败:".$ret['errcode'].$ret['errmsg'],98); }else { return $ret; } } /** * @function 创建发票卡券模板 * @param $appId * @param $params { "invoice_info": { "base_info": { "logo_url": "http://mmbiz.qpic.cn/mmbiz/1.jpg", //发票商家 LOGO "title": "xx公司", //收款方(显示在列表),上限为 9 个汉字,建议填入商户简称 "custom_url_name": "xyz", //开票平台自定义入口名称,与 custom_url 字段共同使用,长度限制在 5 个汉字内 "custom_url": "xyz", //开票平台自定义入口跳转外链的地址链接 , 发票外跳的链接会带有发票参数,用于标识是从哪张发票跳出的链接 "custom_url_sub_title": "xyz", //显示在入口右侧的 tips ,长度限制在 6 个汉字内 "promotion_url_name": "puname", //发营销场景的自定义入口 "promotion_url": "purl", //入口跳转外链的地址链接,发票外跳的链接会带有发票参数,用于标识是从那张发票跳出的链接 "promotion_url_sub_title": "ptitle", //显示在入口右侧的 tips ,长度限制在 6 个汉字内 }, "type": " 广东省增值税普通发票 ", //发票类型 "payee": " 测试 - 收款方 ", //收款方(开票方)全称,显示在发票详情内。故建议一个收款方对应一个发票卡券模板 } } * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException */ public function getInvoiceCardId($appId,$params) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row) { $invoiceCardId = empty($row['invoice_card_id']) ? null : $row['invoice_card_id']; if($invoiceCardId) { return $invoiceCardId; }else { $token = $this->getAccessToken($appId); $url = "https://api.weixin.qq.com/card/invoice/platform/createcard?access_token={$token}"; $curl = new CUrl(30); $curlRes = $curl->post($url,$params); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$params : ' . $params); \PhalApi\DI()->logger->error('$res : ' . json_encode($curlRes)); if(!empty($ret['errcode'])) { throw new RemoteServiceException("创建发票卡券模板失败:".$ret['errcode'].$ret['errmsg'],98); }else { $invoiceCardId = $ret['card_id']; $updateData = array('invoice_card_id' => $invoiceCardId); $params = array('wx_appid' => $appId); $appIdModel->updateData($updateData, $params); return $invoiceCardId; } } }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @param $appId * @param $uid * @param $order_no * @param $ticket_no * @return bool|mixed * @throws LocalServiceException * @throws RemoteServiceException * * @desc 小程序开具电子发票的步骤如下: 1 提前获取开票平台标识s_pappid,因为同一个开票平台的s_pappid都相同,所以获取s_pappid的操作只需要进行一次。不同接入模式获取s_pappid的方法略有不同: 如果商户接入模式为“自建平台模式”:s_pappid通过调用调用获取自身开票平台识别码接口获得 2 商户获取获取access_token。调用方法见获取access_token; 3 设置商户联系方式。调用方法见设置商户联系方式。注意,本步骤不能忽略,否则将造成下一步获取授权页报错; 4 商户获取授权页ticket。调用方法见获取授权页ticket; 5 商户获取授权页url,上一步获取的授权页ticket将作为参数传入。另外,本环节里面作为参数传入的order_id要注意保留,传递给开票平台作为向用户提供电子发票的依据。调用方法见获取授权页链接; 6 在小程序开票按钮的点击事件上部署跳转到小程序授权页的逻辑。上一步获得的auth_url和开票小程序appid要作为参数传入。调用方法见小程序打开授权页; 7 商户在后台等待接收用户的授权完成事件,获取授权事件方法见收取授权完成事件推送; 8 创建发票卡券模板。发票卡券模板应和背后的开票主体构成一一对应关系,便于后续若开票主体发生变化时,可以便捷修改。调用方法见创建发票卡券模板; 9 在自建发票平台开具电子发票; 10 上传发票PDF文件。此步骤获得的s_media_id起到关联PDF和发票卡券的作用,将作为参数在下一步的插卡接口中传入。调用方法见上传PDF; 11 将电子发票添加到用户微信卡包。调用方法见将电子发票卡券插入用户卡包。 */ public function getWxAuthUrl($appId,$uid,$order_no,$ticket_no) { $orderTicketModel = new ModelOrderTickets(); $ticketRow = $orderTicketModel->getTicket($order_no,$ticket_no); if(empty($ticketRow)){ throw new LocalServiceException("操作异常。订单号:{$order_no}票号:{$ticket_no}的票不存在。"); } $order_id = $order_no.'_'.$ticket_no; $money = $ticketRow['fee']; $timestamp = time(); $source = 'wxa'; $redirect_url = ''; $type = '1'; //1.解析InvoiceUrl中的s_pappid参数值 $s_pappid = ''; $invoiceUrl = $this->getInvoiceUrl($appId); $invoiceUrlArr = parse_url($invoiceUrl); parse_str($invoiceUrlArr['query']); $invoiceUrlSpappid = $s_pappid; //3 $this->setContact($appId,30); //4 $authTicket = $this->getTicket($appId); return $this->getAuthUrl($appId,$invoiceUrlSpappid,$order_id,$money,$timestamp,$source,$redirect_url,$authTicket,$type); } /** * @function 发送已经申请第三方成功的发票到用户微信卡包 * @param $appId * @return array * @throws LocalServiceException * @throws RemoteServiceException */ public function sendInvoice($appId) { $orderTicketsModel = new ModelOrderTickets(); $appIdModel = new AppIDModel(); $appIdRow = $appIdModel->fetchByWxAppId($appId); if(!$appIdRow) { throw new LocalServiceException('系统异常:配置信息错误。'); } $cardParams = array( 'invoice_info'=>array( 'type'=>'广东省增值税普通发票', 'payee'=>' 测试 - 收款方', 'base_info'=>array( 'logo_url'=>'http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFNjakmxibMLGWpXrEXB33367o7zHN0CwngnQY7zb7g/0', 'title'=>'空港快线', ) ) ); $invoiceCardId = $this->getInvoiceCardId($appId,json_encode($cardParams,JSON_UNESCAPED_UNICODE)); $wxAccountAppId = $appIdRow['wx_account_appid']; //获取AppConfig配置信息 $appConfigModel = new ModelAppConfig(); $appConfigRes = $appConfigModel->getAppConfig(); if(empty($appConfigRes)){ throw new LocalServiceException("系统异常:AppConfig配置信息有误"); } $appParams = json_decode($appConfigRes['app_params'],true); $invoiceModel = new ModelInvoice(); $preSendInvoices = $invoiceModel->getPreSendInvoice(); $invoiceTotal = count($preSendInvoices); $successCount = 0; $failCount = 0; foreach ($preSendInvoices as $preSendInvoice) { $invoice_detail = json_decode($preSendInvoice['invoice_detail'],true); $info = array( array( 'name'=>$invoice_detail['XMMC'], 'num'=>$invoice_detail['XMSL'], 'unit'=>$invoice_detail['DW'], 'price'=>$invoice_detail['XMDJ']*100, ) ); $invoice_user_data = array( 'fee'=>$preSendInvoice['invoice_price'], 'title'=>$preSendInvoice['receiver_name'], 'billing_time'=>$preSendInvoice['invoice_time'], 'billing_no'=>$preSendInvoice['invoice_code'], 'billing_code'=>$preSendInvoice['invoice_no'], 'fee_without_tax'=>$preSendInvoice['no_invoice_price'], 'tax'=>$preSendInvoice['total_tax'], 's_pdf_media_id'=>$preSendInvoice['pdf_media_id'], 'check_code'=>$preSendInvoice['verify_code'], 'buyer_number'=>$preSendInvoice['tax_number'], 'buyer_address_and_phone'=>$preSendInvoice['address'].$preSendInvoice['telephone'], 'buyer_bank_account'=>$preSendInvoice['bank_name'].$preSendInvoice['bank_account'], 'seller_number'=>$appParams['KP_NSRSBH'], 'seller_address_and_phone'=>$appParams['XHF_DZ'].$appParams['XHF_DH'], 'seller_bank_account'=>$appParams['XHF_YHZH'], 'cashier'=>$appParams['SKR'], 'maker'=>$appParams['KPR'], 'info'=>$info, ); $card_ext = array( 'nonce_str'=> rand(1000000000,9999999999), 'user_card'=>array('invoice_user_data'=>$invoice_user_data), ); $params = array( 'order_id'=>$preSendInvoice['order_no'].'_'.$preSendInvoice['ticket_no'], 'card_id'=>$invoiceCardId, 'appid'=>$wxAccountAppId, 'card_ext'=>$card_ext, ); $token = $this->getAccessToken($appId); $url = "https://api.weixin.qq.com/card/invoice/insert?access_token={$token}"; $curl = new CUrl(30); $curlRes = $curl->post($url,json_encode($params,JSON_UNESCAPED_UNICODE)); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->error('$appId : ' . $appId); \PhalApi\DI()->logger->error('$url : ' . $url); \PhalApi\DI()->logger->error('$params : ' . json_encode($params)); \PhalApi\DI()->logger->error('$res : ' . json_encode($curlRes)); if(!empty($ret['errcode'])) { $failCount++; }else { $invUpdateData = array('state'=>7); $invUpdateParams = array('order_no'=>$preSendInvoice['order_no'],'ticket_no'=>$preSendInvoice['ticket_no']); $invoiceModel->updateInvoice($invUpdateParams,$invUpdateData); //改变电子票列表的申请发票状态 $ticUpdateData = array('invoice_state'=>7); $ticUpdateParams = array('order_no'=>$preSendInvoice['order_no'],'ticket_no'=>$preSendInvoice['ticket_no']); $orderTicketsModel->updateTicket($ticUpdateParams,$ticUpdateData); $successCount++; } } return array('invoiceTotal'=>$invoiceTotal,'successCount'=>$successCount,'failCount'=>$failCount); } /** * @function 查询授权完成状态 * * @desc * 本接口的调用场景包括两个: * 一、若商户在某次向用户展示授权页后经过较长时间仍未收到授权完成状态推送,可以使用本接口主动查询用户是否实际上已完成授权,只是由于网络等原因未收到授权完成事件 * 二、若商户向用户展示的授权页为type=1类型,商户在收到授权完成事件推送后需要进一步获取用户的开票信息,也可以调用本接口。 * * @param $appId * @return mixed * @throws LocalServiceException * @throws RemoteServiceException */ public function getAuthData($appId) { //1.获取AccessToken $token = $this->getAccessToken($appId); //2.解析InvoiceUrl中的s_pappid参数值 $s_pappid = ''; $invoiceUrl = $this->getInvoiceUrl($appId); $invoiceUrlArr = parse_url($invoiceUrl); parse_str($invoiceUrlArr['query']); $invoiceUrlSpappid = $s_pappid; //3.获取已经申请航信的发票,上传PDF $invoiceModel = new ModelInvoice(); $preSendInvoices = $invoiceModel->getAuthInvoice(); $invoiceTotal = count($preSendInvoices); $successCount = 0; $failCount = 0; foreach ($preSendInvoices as $preSendInvoice) { $order_id = $preSendInvoice['order_no'].'_'.$preSendInvoice['ticket_no']; $url = "https://api.weixin.qq.com/card/invoice/getauthdata?access_token={$token}"; $params = json_encode(array('s_pappid' => $invoiceUrlSpappid, 'order_id' => $order_id)); $curl = new CUrl(30); $curlRes = $curl->post($url, $params); $ret = json_decode($curlRes, true); \PhalApi\DI()->logger->info('$url : ' . $url); \PhalApi\DI()->logger->info('$params : ' . $params); \PhalApi\DI()->logger->info('$ret : ' . json_encode($ret)); if(!empty($ret['errcode']) and $ret['invoice_status']!= 'auth success'){ $failCount++; continue; } if(!empty($ret['user_auth_info']['user_field']))//个人发票 { $invoice_type = '2'; $receiver_name = !empty($ret['user_auth_info']['user_field']['title'])?$ret['user_auth_info']['user_field']['title']:''; $telephone = !empty($ret['user_auth_info']['user_field']['phone'])?$ret['user_auth_info']['user_field']['phone']:''; $email = !empty($ret['user_auth_info']['user_field']['email'])?$ret['user_auth_info']['user_field']['email']:''; $updateData = array( 'state'=>'2', 'invoice_type'=>$invoice_type, 'receiver_name'=>$receiver_name, 'telephone'=>$telephone, 'email'=>$email, ); }elseif (!empty($ret['user_auth_info']['biz_field']))//企业发票 { $invoice_type = '1'; $receiver_name = !empty($ret['user_auth_info']['biz_field']['title'])?$ret['user_auth_info']['biz_field']['title']:''; $tax_number = !empty($ret['user_auth_info']['biz_field']['tax_no'])?$ret['user_auth_info']['biz_field']['tax_no']:''; $address = !empty($ret['user_auth_info']['biz_field']['addr'])?$ret['user_auth_info']['biz_field']['addr']:''; $telephone = !empty($ret['user_auth_info']['biz_field']['phone'])?$ret['user_auth_info']['biz_field']['phone']:''; $bank_name = !empty($ret['user_auth_info']['biz_field']['bank_type'])?$ret['user_auth_info']['biz_field']['bank_type']:''; $bank_account = !empty($ret['user_auth_info']['biz_field']['bank_no'])?$ret['user_auth_info']['biz_field']['bank_no']:''; $updateData = array( 'state'=>'2', 'invoice_type'=>$invoice_type, 'receiver_name'=>$receiver_name, 'tax_number'=>$tax_number, 'address'=>$address, 'telephone'=>$telephone, 'bank_name'=>$bank_name, 'bank_account'=>$bank_account, ); }else{ $updateData = array(); } $params = array('id' => $preSendInvoice['id']); $invoiceModel->updateInvoice($params,$updateData); $orderTicketModel = new ModelOrderTickets(); $ticParams = array('order_no'=>$preSendInvoice['order_no'],'ticket_no'=>$preSendInvoice['ticket_no']); $ticUpdateData = array('invoice_state'=>'2'); $orderTicketModel->updateTicket($ticParams,$ticUpdateData); $successCount++; } return array('invoiceTotal'=>$invoiceTotal,'successCount'=>$successCount,'failCount'=>$failCount); } /** * @function 上传PDF * @desc * 商户或开票平台可以通过该接口上传PDF。PDF上传成功后将获得发票文件的标识,后续可以通过插卡接口将PDF关联到用户的发票卡券上,一并插入到收票用户的卡包中。 * 注意:若上传成功的PDF在三天内没有被关联到发票卡券发送到用户卡包上,将会被清理。若商户或开票平台需要在三天后再关联发票卡券的话,需要重新上传。 * * @param $appId * @return array * @throws LocalServiceException * @throws RemoteServiceException */ public function setPDF($appId) { $appIdModel = new AppIDModel(); $row = $appIdModel->fetchByWxAppId($appId); if($row) { //1.获取AccessToken $token = $this->getAccessToken($appId); //2. $url = "https://api.weixin.qq.com/card/invoice/platform/setpdf?access_token={$token}"; //3.获取已经申请航信的发票,上传PDF $invoiceModel = new ModelInvoice(); $preSendInvoices = $invoiceModel->getPreSendInvoice(); $invoiceTotal = count($preSendInvoices); $successCount = 0; $failCount = 0; foreach ($preSendInvoices as $preSendInvoice) { $fileName = $preSendInvoice['file_name'].'.pdf'; $fileUrl = $preSendInvoice['file_url']; $fields = array( 'type' => 'pdf', 'filename' => $fileName, 'filesize' => '', 'offset' => 0, 'filetype' => '.pdf', 'originName' => $fileName, 'upload'=>file_get_contents($fileUrl) ); $ret = $this->postPDF($url,$fields); \PhalApi\DI()->logger->error('$res : ' . json_encode($ret)); if($ret === false or !empty($ret['errcode'])){ $failCount++; continue; } $PDFMediaId = $ret['s_media_id']; $updateData = array('pdf_media_id' => $PDFMediaId); $params = array('id' => $preSendInvoice['id']); $invoiceModel->updateInvoice($params,$updateData); $successCount++; } return array('invoiceTotal'=>$invoiceTotal,'successCount'=>$successCount,'failCount'=>$failCount); }else { throw new LocalServiceException("账户信息不存在或配置有误",99); } } /** * @function 发送pdf * @param $url string * @param $param array * @return mixed */ private static function postPDF($url,$param) { $delimiter = uniqid(); $data = ''; $eol = "\r\n"; $upload = $param['upload']; unset($param['upload']); foreach ($param as $name => $content) { $data .= "--" . $delimiter . "\r\n" . 'Content-Disposition: form-data; name="' . $name . "\"\r\n\r\n" . $content . "\r\n"; } // 拼接文件流 $data .= "--" . $delimiter . $eol . 'Content-Disposition: form-data; name="pdf"; filename="' . $param['filename'] . '"' . "\r\n" . 'Content-Type:application/octet-stream'."\r\n\r\n"; $data .= $upload . "\r\n"; $data .= "--" . $delimiter . "--\r\n"; $post_data = $data; $curl = curl_init($url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // stop verifying certificate curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data); curl_setopt($curl, CURLOPT_HTTPHEADER, [ "Content-Type: multipart/form-data; boundary=" . $delimiter, "Content-Length: " . strlen($post_data) ]); $response = curl_exec($curl); curl_close($curl); $info = json_decode($response, true); return $info; } } 调用示例 1.获取授权链接$invoiceClass = new Invoice(); $appId = \PhalApi\DI()->config->get('app.kgkx.wxAppId');//用于获取对应(zc2_ticket_appid)的配置信息 $uid = $this->uid;//车票所属的用户openid $order_no = $this->orderNo;//车票所属的订单号 $ticket_no = $this->ticketNo;//车票号 //getWxAuthUrl方法已经包含了“大概逻辑”里的前5步,最后只要将授权链接在小程序打开即可 $invoiceClass ->getWxAuthUrl($appId,$uid,$order_no,$ticket_no); 返回结果{ "ret": 200, "data": { "errcode": 0, "errmsg": "ok", "auth_url": "pages/auth/auth?s_pappid=d3g1Y2Q4NTI3NWI4NDdlM2I2X7xcO5NPKJ9cUhYRLlygl8Dc2ZUl9PjYwiOwmLytYQwA&appid=wx5cd85275b847e3b6&num=1&o1=201904191345503490_0028657333&m1=15&t1=1557816628&source=wxa&type=1&signature=75dcbd921494457089d6c59534f9769624802b27", "appid": "wx9db2c16d0633c2e7" }, "msg": "" } 小程序打开授权链接代码//发票授权 invoiceAuth: function () { wx.navigateToMiniProgram({ appId: 'wx9db2c16d0633c2e7', path: 'pages/auth/auth?s_pappid=d3g1Y2Q4NTI3NWI4NDdlM2I2X7xcO5NPKJ9cUhYRLlygl8Dc2ZUl9PjYwiOwmLytYQwA&appid=wx5cd85275b847e3b6&num=1&o1=201904191345503490_0028657333&m1=15&t1=1557816628&source=wxa&type=1&signature=75dcbd921494457089d6c59534f9769624802b27', success(res) { console.log('navigateToMiniProgram success:', res) }, fail(error) { console.log('navigateToMiniProgram fail:', error) }, complete(res) { console.log('navigateToMiniProgram complete:', res) } }) } 值得一提的是,这里的appId需要再app.json文件先配置好 "navigateToMiniProgramAppIdList": [ "wx9db2c16d0633c2e7" ] 3.主动查询授权状态$invoiceClass = new Invoice(); $appId = \PhalApi\DI()->config->get('app.kgkx.wxAppId');//用于获取对应(zc2_ticket_appid)的配置信息 $invoiceClass ->getAuthData($appId); 上传PDF说明: 这里会用到定时任务的方式。请求第三方开票平台后,会返回一个发票PDF文件的地址,我们先用file_get_contents下载PDF文件,再通过数据格式使用multipart/form-data上传到微信。上传成功后,会返回一个s_media_id, 这个在发送发票到用户卡包的时候用到,需要对应发票记录来存。这里面的代码参考了:https://blog.51cto.com/suiwnet/2125883$invoiceClass = new Invoice(); $appId = \PhalApi\DI()->config->get('app.kgkx.wxAppId');//用于获取对应(zc2_ticket_appid)的配置信息 $invoiceClass ->setPDF($appId); 5.发送发票到用户卡包$invoiceClass = new Invoice(); $appId = \PhalApi\DI()->config->get('app.kgkx.wxAppId');//用于获取对应(zc2_ticket_appid)的配置信息 $invoiceClass ->sendInvoice($appId); 总结开发票的流程到这里已经完成。需要完善的是,发票冲红的问题。存在一个问题,access_token过了一段时间(这段时间小于返回的有效时间-200秒)之后就过期了,这里也需要继续完善。(2019-08-20)问题已经确认,是因为在多个地方用到了这个公众号的access_token,每用一次,上一个就可能过期,解决办法是,加一个验证checkAccessToken($access_token)。————————————————版权声明:本文为CSDN博主「13xs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_34739614/article/details/90208838
2021年06月21日
38 阅读
0 评论
0 点赞
2021-06-21
腾讯客服网页联系方式
【腾讯科技】腾讯客服:尊敬的微信支付商户您好,若您在商户开发测试过程中遇到相关技术问题,您可以使用电脑访问:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/index.shtml,点击右侧“技术咨询”并使用微信扫码登录,即可咨询开发相关问题,工作时间为:工作日10:00-18:00。如在工作时间外,您也可登录:https://developers.weixin.qq.com/community/pay开放社区发帖反馈,或请您按以下格式发送邮件,会有相关工作人员邮件答复您。收件人邮件地址:wepayTS@tencent.com发送格式:主题:微信号+商户名称,及“开发问题”字样邮件内容: 1)商户号: 2)商户名称: 3)问题描述: 4)错误提示: 5)相关截图:温馨提示:确保已参考开发文档,且查看了常见技术报错,仍无法解决;请您仔细填写邮件主题、内容,微信团队会在收到邮件后1-2个工作日审核。
2021年06月21日
28 阅读
0 评论
1 点赞
2021-06-18
微信电子发票--“自建平台模式”--小程序开票
首先开通电子发票功能点击连接 : 开通电子发票微信官方接口文档连接 :商户接口列表 连接 :开票平台接口列表 研究了几天之后,发现无论如何都需要和开票平台合作 看下对应的接口文档 14 统一开票接口-开具蓝票接口说明 对于使用微信电子发票开票接入能力的商户,在公众号后台选择任何一家开票平台的套餐,都可以使用本接口实现电子发票的开具。请求方式 请求URL:https://api.weixin.qq.com/card/invoice/makeoutinvoice?access_token={access_token} 目前主流的开票平台 如下,我们尝试打过去一家,首年收费2.5W,超出了预算了 连接 : 目前和微信电子发票互通的开票平台
2021年06月18日
40 阅读
0 评论
1 点赞
2021-06-13
CentOS 8 安装 FFmpeg
FFmpeg是一套免费的开源计算机程序,它提供了录制、转换以及流化音视频的完整解决方案。FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。本文介绍在CentOS 8上安装FFmpeg的方法。注:官方Yum源不包含 FFmpeg 的库,所以需要安装其他仓库来实现。 1、添加RPMfusion仓库 在CentOS 8上安装FFmpeg,需要先添加RPMfusion仓库:sudo yum install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm2、安装FFmpeg 添加了RPMfusion仓库后,运行以下两条命令安装FFmpeg:sudo yum install http://rpmfind.net/linux/epel/7/x86_64/Packages/s/SDL2-2.0.10-1.el7.x86_64.rpm sudo yum install ffmpeg若有需要可以再安装开发库:sudo yum -y install ffmpeg-devel3、检查FFmpeg是否安装成功rpm -qi ffmpeg ffmpeg -version
2021年06月13日
39 阅读
0 评论
0 点赞
2021-06-11
Linux系统进入redis并查询值
1.进入redisredis-cli2.获取权限auth 你的redis密码3.得到redis中存储的所有key值keys *4.获取指定key值的value值get “key值”
2021年06月11日
14 阅读
0 评论
0 点赞
1
2
3
4