微信公众号JSAPI支付(easywechat)

  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'))

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值