微信公众号JSAPI支付(easywechat)_handlepaidnotify-程序员宅基地

技术标签: 微信  

  1. 授权根据code获取用户openid
  2. 此时需要给客户端读取jsApiList权限 以及返回对应签名、时间戳、字符串等(备注:获取时需要前端给一个下单的完整url路径进行验签否则 客户端会报60032 签名错误)
  3. 根据code调统一下单接口
  4. 拿到prepay_id进行二次验签
  5. 验签完成返回给前端拉起支付
  6. 回调完成 至此流程结束 

配置相关:

公众平台:授权目录来获取openid  安全目录、安全域名 配置客户端的h5链接 关联商户平台

商户平台:支付授权目录(顶级目录即可),api密钥,证书配置(退款、提现使用)


遇到的坑:

给客户端生成配置时url问题一直报签名错误,url规则(必须是完整域名且携带参数并且不得携带#后面的内容),url携带参数时easywechat会自动转换成 xx.com?id=1&params1=xxx&params2=xxx(此时需要str_replace除去无用字符串保证提交过来的url和获取签名后的url是一致的否则也会报签名错误)

客户端调起支付时签名错误(此时在开放平台验签工具是验证通过的),解决:需要拿prepay_id预支付订单id二次验签调用 $app->jssdk->sdkConfig($prepay_id) 返回给客户端 支付调起成功。


代码段:

<?php
namespace app\api\library;

use EasyWeChat\Factory;
use think\Cache;
use think\Config;

class Wechat
{
    protected $config = [];
    protected $siteConfig = [];
    protected $app = null;
    protected static $instance = null;

    /**
     * Wechat constructor.
     * @param $type
     */
    public function __construct($type = 'pay')
    {
        $siteConfig = Config::get('site');
        $this->siteConfig = $siteConfig;
        $this->config = array(
            'app_id'             => $siteConfig['appid'],
            'appid'              => $siteConfig['appid'],
            'mch_id'             => $siteConfig['mch_id'],
            'key'                => $siteConfig['secret'],
            'cert_path'          => $siteConfig['cert_path'],
            'key_path'           => $siteConfig['key_path'],
            'notify_url'         => request()->domain() . '/api' . $siteConfig['notify'],
        );
        $method = $type == 'pay' ? 'payment' : 'officialAccount';
        if ($type == 'officialAccount'){
        }
        $this->config = array_merge(array('secret' => $siteConfig['app_secret']),$this->config);
        $this->app = Factory::$method($this->config);
    }

    /**
     * @param string $type
     * @return static|null
     */
    public static function instance($type = 'pay')
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($type);
        }
        return self::$instance;
    }

    /**
     * 支付实例化
     * @return Pay
     */
    public function pay(){
        return new Pay();
    }

    /**
     * 退款实例化
     * @return Refund
     */
    public function refund(){
        return new Refund();
    }

    /**
     * 构建config
     * @param $apis
     * @param $uri
     * @return mixed
     */
    public function getBuildConfig($apis,$uri){
        $uri = strpos($uri,'amp;') !== false ? str_replace('amp;','',$uri) : $uri;
        $this->app->jssdk->setUrl(str_replace('amp;','',$uri));
        return $this->app->jssdk->buildConfig($apis,true);
    }

    /**
     * @param $prepayIds
     * @return mixed
     */
    public function getJsSdkConfig($prepayIds){
        return $this->app->jssdk->sdkConfig($prepayIds);
    }

    /**
     * 获取openid
     * @param $code
     * @return \Overtrue\Socialite\User
     */
    public function getOpenId($code){
        $secret = $this->config['secret'];
        $appid = $this->config['appid'];
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$appid."&secret=".$secret."&code=".$code."&grant_type=authorization_code";

        $ch = curl_init();
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch,CURLOPT_TIMEOUT,30);

        $content = curl_exec($ch);
        $status = (int)curl_getinfo($ch,CURLINFO_HTTP_CODE);
        if ($status == 404) {
            return $status;
        }
        curl_close($ch);
        return json_decode($content,true);
    }
}
<?php
namespace app\api\library;
use app\api\model\Order;
use think\Cache;

class Pay extends Wechat
{
    /**
     * 统一下单
     * @param $out_trade_no
     * @param $payInfo
     * @param $openId
     * @param $amount
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function handlePay($out_trade_no,$payInfo,$openId,$amount){
        return $this->app->order->unify(array(
            'body'          => $payInfo,
            'out_trade_no'  => $out_trade_no,
            'total_fee'     => (int)($amount * 100),
            'notify_url'    => $this->config['notify_url'], // 支付结果通知网址,如果不设置则会使用配置里的默认地址
            'trade_type'    => 'JSAPI', // 请对应换成你的支付方式对应的值类型
            'openid'        => $openId,
        ));
    }

    /**
     * 支付回调
     * @return mixed
     */
    public function handleNotify(){
        return $this->app->handlePaidNotify(function ($message,$error){
            $order = Order::get(['sn'=>$message['out_trade_no']]);
            if (!$order || $order['status'] == Order::SUCCESS) return true;

            if ($message['return_code'] === 'SUCCESS') {
                if ($message['result_code'] === 'SUCCESS') {
                    $order['status'] = Order::SUCCESS;
                } elseif ($message['result_code'] === 'FAIL') {
                    $order['status'] = 'paid_fail';
                }
            } else {
                return $error('通信失败,请稍后再通知我');
            }
            if ($order->save()){
                return true;
            }
            return false;
        });
    }

}
<?php
namespace app\api\library;

use EasyWeChat\Kernel\Exceptions\InvalidConfigException;

class Refund extends Wechat
{

    /**
     * 根据商户订单号退款
     * @param $no 商户订单号
     * @param $refundNo 退款单号
     * @param $orderAmount 订单金额
     * @param $refundAmount 退款金额
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function handleByRefundNo($no,$refundNo,$orderAmount,$refundAmount){
        try {
            $handel = $this->app->refund->byOutTradeNumber($no,$refundNo,($orderAmount * 100),($refundAmount * 100));
        }catch (InvalidConfigException $e){
            return ['code'=>$e->getCode(),'msg'=>$e->getMessage()];
        }
        return $handel;
    }
}
<?php

//统一下单
Wechat::instance()->pay()->handlePay($out_trade_no,'缴费',$this->auth->getOpenId(),$order['amount']);

//二次验签
Wechat::instance('jsapi')->getJsSdkConfig($submitPay['prepay_id']);

//支付回调
Wechat::instance()->pay()->handleNotify()->send()

//退款
RefundService::instance()->refund()->handleByRefundNo($out_trade_no,$refund_no,$order_amount,$refund_amount);

//获取微信接口权限
Wechat::instance('jsapi')->getBuildConfig(array('scanQRCode','chooseWXPay'),$this->request->post('url'))

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lidahanga_/article/details/120589477

智能推荐

基于Matlab实现标准规划问题求解(源码+数据)_matlab规划求解-程序员宅基地

文章浏览阅读426次。基于Matlab实现标准规划问题求解(源码+数据)_matlab规划求解

.Net Framework 4.6.2升级/ 指定的信任提供程序不认识或不支持使用者的指定格式_指定的信任提供程序不认识或不支持使用者的指定格式。-程序员宅基地

文章浏览阅读1.5w次,点赞3次,收藏2次。1.打开官方地址:https://www.microsoft.com/zh-cn/download/details.aspx?id=53345选择“下载”:2.下载文件,点击下载完成的文件,点击运行:3.勾选“同意安装”:4.按道理应该安装成功了。如果出现如下安装提示:那么建议使用离线版的进行安装:https://www.microsof_指定的信任提供程序不认识或不支持使用者的指定格式。

深入讲解Java!专属于Java程序员的学习福音,Java面试题汇总-程序员宅基地

文章浏览阅读49次。前言众所周知,Java开发人员的生存环境可谓是与以前大相径庭,以IT行业发展来说,在十几年前的时候,IT行业的技术人才是稀缺的,程序员最初的招聘行情,只要你会敲“holle world”、会点技术,能做出点东西,就能入职月薪过万的大厂岗位。而到了今年2021年,初级人才已经不稀缺了。“IT初级人才招聘市场上”除了每年新入行的人,还有几年内、大几年内入行后技术没有进步的人,这些人被辞退、或跳槽找工作,找的也是“IT初级程序员”岗位……以互联网巨头阿里的招聘信息来看,首先对于工作经验有一定的要求,在我看来

c++11/14/17标准你了解多少_c++ 11 和 14-程序员宅基地

文章浏览阅读7.6w次,点赞15次,收藏187次。我们常用的c++,你对它的标准了解多少呢?本文就带你一探c++11新标准。官网链接:https://msdn.microsoft.com/zh-cn/library/hh567368.aspx#featurelist C++11 功能列表 C++11 核心语言功能表 C++11 核心语言功能表:并发 C++11 核心语言功能:C99 ..._c++ 11 和 14

python event 事件类 events.py 类_python event csdn-程序员宅基地

文章浏览阅读4.2k次。python event 事件类 events.py 类Locust源码分析之events.py模块(5)https://blog.csdn.net/biheyu828/article/details/84983780eventpy —— Python 事件派发和回调代码库https://zhuanlan.zhihu.com/p/107190607eventpy —— Python 事件..._python event csdn

机器人制作开源方案 | 核酸检测辅助机器人-程序员宅基地

文章浏览阅读1k次,点赞27次,收藏20次。不惧高温、严寒,且精确度高,即使每天都在做重复性的工作,也不会感到劳累。核酸采样模块首先医护人员把棉签放入六自由度的机器臂的末端夹持器里,然后按下采样按键,机械臂末端先夹持住棉签,然后移动到采样区域,再进行核酸采样,采样过后移动到存放采样棉签的试管瓶口的上端,夹持器放下棉签到试管里,最后移动到初始的位置进行下一次采样,程序流程图如下所示。通过先接近,再接近,后采样的策略,本设计采用定点采样,做核酸人员通过一次性采样嘴再机器人采样口等待核酸采样,然后机械臂先接近采样口,再接近人的口腔,最后进行核酸采样。

随便推点

springboot 与swagger整合出现Unable to infer base url.This is common when using dynamic的解决办法_springboot 集成swagger unable to infer base url. thi-程序员宅基地

文章浏览阅读8.2k次,点赞2次,收藏2次。今天在springboot与swagger整合测试的时候跳出如下所示界面经查资料发现有两种解决办法,1.直接把@EnableSwagger2注解加在主启动类就可以,这样虽然能解决问题,但是这样会扫到使用的框架的接口,这种方法要慎用。2.主启动类加上@ComponentScan("swagger配置类所在包"),以保证配置类被扫描到最后解决问题之后就可以访问 你的配置文..._springboot 集成swagger unable to infer base url. this is common when using d

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/poi/util/POILogFactory-程序员宅基地

文章浏览阅读3.7k次。Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/poi/util/POILogFactoryException in thread "main" java.lang.NoClassDefFoundError: org/apache/poi/util/POILogFactory at org.a..._java.lang.noclassdeffounderror: org/apache/poi/util/poilogfactory

我常用的Latex中文报告模板(一)-程序员宅基地

文章浏览阅读1.6k次。不得不说,使用Latex编写文档效率会提升很多。但是,如果没有好的模板,自己从零开始动手完成一份Latex文档还是得花费不少时间和精力的,所以,为了提高文档和技术报告的编写效率,我为自己准备了以下的Latex模板,并附图如下(注意,在Texlive中需要自己安装黑体(hei)和楷体(kai)等中文字体,具体安装方法我会在下一篇博客中介绍。\documentclass[a4paper, 11..._用latex写报告模版包括但不限于标题、字号和正文

SAP 后继物料简介-程序员宅基地

文章浏览阅读555次。通常情况下当一个物料被停用以后需要更换成另外的一个物料,我们首先想到的就是去更改BOM,的确这也是一种可行的办法,但是这样做会带来BOM的维护人员的工作量,另一个方面是不知道在什么时间点去更改BOM,需要替换的物料在什么时候消耗完。我们可以看到100206的这个物料在被上面的需求消耗一个以后,还剩下库存1PC,是不满足需求的,紧接着根据物料主数据上面的后继物料100208被扣除库存需求1PC。所以在物料永久的替换的业务中,我们可以通过物料主数据上面的后继物料来实现,当物料库存消耗完以后替换成新物料。_sap 后继物料

跨考计算机之路,2014考研心得:零基础跨考之路-程序员宅基地

文章浏览阅读216次。2014考研心得:零基础跨考之路2013-12-02 14:03来源:新东方网作者:我就读于华南农业大学生态学专业,报考的是华南理工大学经济与贸易学院的国民经济学专业。华南理工大学是一所985、211工程院校,而我是跨校跨专业考研,此次考研成绩总分387,政治71,英语一65,数学三117,专业课134。我准备考研是从2012年的三月份,之前一直在徘徊着,不确定要不要跨专业,后来在家人的鼓励下毅然..._生态学跨考计算机

OpenMPI(一) 点对点通信-程序员宅基地

文章浏览阅读579次。为什么80%的码农都做不了架构师?>>> ..._节点如何在 openmpi 中通信

推荐文章

热门文章

相关标签