可能是我用过的最优雅的「 Alipay 和 WeChat 的支付 SDK」了

开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。

github: https://github.com/yansongda/pay
gitee: https://gitee.com/yansongda/pay

欢迎 Star,欢迎 PR!

潜水了这么久,自己学习了很多,是时候回馈社区了!

晚些时候将开发 laravel 适配包!
laravel 适配包 在这里

特点

  • 命名不那么乱七八糟
  • 隐藏开发者不需要关注的细节
  • 根据支付宝、微信最新 API 开发而成
  • 高度抽象的类,免去各种拼json与xml的痛苦
  • 符合 PSR 标准,你可以各种方便的与你的框架集成
  • 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关
  • 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的

以上部分引用了@overtrue 的部分说明,在此感谢!:satisfied:

运行环境

  • PHP 5.6+
  • composer

支持的支付网关

由于各支付网关参差不齐,所以我们抽象了两个方法 driver()gateway()

两个方法的作用如下:

driver() : 确定支付平台,如 alipay,wechat;

gateway(): 确定支付网关。通过此方法,确定支付平台下的支付网关。例如,支付宝下有 「电脑网站支付」,「手机网站支付」,「APP 支付」三种支付网关,通过传入 web,wap,app 确定。

详细思路可以查看源代码。

1、支付宝

  • 电脑支付
  • 手机网站支付
  • APP 支付

SDK 中对应的 driver 和 gateway 如下表所示:

driver gateway 描述
alipay web 电脑支付
alipay wap 手机网站支付
alipay app APP 支付

2、微信

  • 公众号支付
  • 小程序支付
  • H5 支付
  • 扫码支付
  • 刷卡支付

SDK 中对应的 driver 和 gateway 如下表所示:

driver gateway 描述
wechat mp 公众号支付
wechat miniapp 小程序支付
wechat wap H5 支付
wechat scan 扫码支付
wechat pos 刷卡支付

支持的方法

所有网关均支持以下方法

  • pay(array $config_biz)
    说明:支付接口
    参数:数组类型,订单业务配置项,包含 订单号,订单金额等
    返回:mixed 详情请看「支付网关配置说明与返回值」一节。

  • refund(array|string $config_biz, $refund_amount = null)
    说明:退款接口
    参数:$config_biz 为字符串类型仅对支付宝支付有效,此时代表订单号,第二个参数为退款金额。
    返回:mixed 退款成功,返回 服务器返回的数组;否则返回 false;

  • close(array|string $config_biz)
    说明:关闭订单接口
    参数:$config_biz 为字符串类型时代表订单号,如果为数组,则为关闭订单业务配置项,配置项内容请参考各个支付网关官方文档。
    返回:mixed 关闭订单成功,返回 服务器返回的数组;否则返回 false;

  • find(string $out_trade_no)
    说明:查找订单接口
    参数:$out_trade_no 为订单号。
    返回:mixed 查找订单成功,返回 服务器返回的数组;否则返回 false;

  • verify($data, $sign = null)
    说明:验证服务器返回消息是否合法
    参数:$data 为服务器接收到的原始内容,$sign 为签名信息,当其为空时,系统将自动转化 $data 为数组,然后取 $data['sign']
    返回:mixed 验证成功,返回 服务器返回的数组;否则返回 false;

安装

composer require yansongda/pay

使用说明

0、一个完整的例子:

<?php

namespace App\Http\Controllers;

use Yansongda\Pay\Pay;
use Illuminate\Http\Request;

class PayController extends Controller
{
    protected $config = [
        'alipay' => [
            'app_id' => '2016082000295641',
            'notify_url' => 'http://yansongda.cn/alipay_notify.php',
            'return_url' => 'http://yansongda.cn/return.php',
            'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB',
            'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==',
        ],
    ];

    public function index()
    {
        $config_biz = [
            'out_trade_no' => time(),
            'total_amount' => '1',
            'subject'      => 'test subject',
        ];

        $pay = new Pay($this->config);

        return $pay->driver('alipay')->gateway()->pay($config_biz);
    }

    public function return(Request $request)
    {
        $pay = new Pay($this->config);

        return $pay->driver('alipay')->gateway()->verify($request->all());
    }

    public function notify(Request $request)
    {
        $pay = new Pay($this->config);

        if ($pay->driver('alipay')->gateway()->verify($request->all())) {
            file_put_contents(storage_path('notify.txt'), "收到来自支付宝的异步通知\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单号:' . $request->out_trade_no . "\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单金额:' . $request->total_amount . "\r\n\r\n", FILE_APPEND);
        } else {
            file_put_contents(storage_path('notify.txt'), "收到异步通知\r\n", FILE_APPEND);
        }

        echo "success";
    }
}
<?php

namespace App\Http\Controllers;

use Yansongda\Pay\Pay;
use Illuminate\Http\Request;

class PayController extends Controller
{
    protected $config = [
        'wechat' => [
            'app_id' => 'wxb3f6d0xxxxxxxd',
            'mch_id' => '1457768302',
            'notify_url' => 'http://yansongda.cn/wechat_notify.php',
            'key' => 'mF2suE9sU6Mkxxxxxxx5645645',
            'cert_client' => './apiclient_cert.pem',
            'cert_key' => './apiclient_key.pem',
        ],
    ];

    public function index()
    {
        $config_biz = [
            'out_trade_no' => 'e2',
            'total_fee' => '0.01',
            'body' => 'test body',
            'spbill_create_ip' => '14.213.156.207',
            'openid' => 'onkVf1FjWS5SBIihS-123456_abc',
        ];

        $pay = new Pay($this->config);

        return $pay->driver('wechat')->gateway('mp')->pay($config_biz);
    }

    public function notify(Request $request)
    {
        $pay = new Pay($this->config);
        $verify = $p->driver('wechat')->gateway('mp')->verify($request->getContent());

        if ($verify) {
            file_put_contents('notify.txt', "收到来自微信的异步通知\r\n", FILE_APPEND);
            file_put_contents('notify.txt', '订单号:' . $verify['out_trade_no'] . "\r\n", FILE_APPEND);
            file_put_contents('notify.txt', '订单金额:' . $verify['total_fee'] . "\r\n\r\n", FILE_APPEND);
        } else {
            file_put_contents(storage_path('notify.txt'), "收到异步通知\r\n", FILE_APPEND);
        }

        echo "success";
    }
}

1、准备配置参数

$config = [
    'alipay' => [
        'app_id' => '',             // 支付宝提供的 APP_ID
        'ali_public_key' => '',     // 支付宝公钥,1行填写
        'private_key' => '',        // 自己的私钥,1行填写
    ],
];
$config_biz = [
    'out_trade_no' => '12',                 // 订单号
    'total_amount' => '13',                 // 订单金额,单位:元
    'subject' => 'test subject',   // 订单商品标题
];

2、在代码中使用

$pay = new Pay($config);
return $pay->dirver('alipay')->gateway('web')->pay($config_biz);

错误

使用非跳转接口(如, refund 接口,close 接口)时,如果在调用相关支付网关 API 时有错误产生,会抛出 GatewayException 错误,可以通过 $e->getMessage() 查看,同时,也可通过 $e->raw 查看调用 API 后返回的原始数据,该值为数组格式。

支付网关配置说明与返回值

由于支付网关不同,每家参数参差不齐,为了方便,我们抽象定义了两个参数:$config,$config_biz,分别为全局参数,业务参数。但是,所有配置参数均为官方标准参数,无任何差别。

「业务参数」为订单相关的参数,「全局参数」为除订单相关参数以外的全局性参数。

具体参数列表请查看每个支付网关的使用说明。

请传送至github,查看详细内容

代码贡献

由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。

如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,欢迎 Fork 并提交 PR!

感谢大家支持!

LICENSE

MIT

Coding My Life。
本帖已被设为精华帖!
本帖由 Summer 于 6年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 89

几个小建议

  • 同四楼,标题再升华下;
  • 代码的风格,underscore的方式受众还是少于camel
  • 单元测试补上,对于长期维护的项目来说单元测试很重要,不止是规范,还是用来检查别人pr的基础验证
  • composer.lock 文件从版本库中移除
  • 继续抽象 这个得细说明下,和业务无关和代码生态设计有关:

第四点说明

  • 配置问题,下面代码,将不同驱动的写在一个大数组里传给SDK, 这样确实够简单但也是典型缺乏设计的表现
    $config = [
    'alipay' => [
        'app_id' => '2016082000295641',
        'notify_url' => 'http://yansongda.cn/alipay_notify.php',
        'return_url' => 'http://yansongda.cn/return.php',
        'ali_public_key' => '',
        'private_key' => =',
    ],
    ];
    $config_biz = [
    'out_trade_no' => '1',
    'total_amount' => '1',
    'subject' => 'test subject',
    ];
    $pay->pay($config_biz );

如果研究过amazon和paypal登公司sdk的话,你可能会经常遇到一个对象Credential 这个单词执意过来就是“凭据”,sdk和api交互的身份信息;所以如果这么改一下,我会觉得好一点

$credential = new Pay\Alipay\Context\Credential(APP_ID,  ALI_PUBLICK_KEY);
$payment= new  Pay\Alipay\Client($credential);

$payment->purchase(new Product(Currency::RMB, 10.20)); //收款

$order = new Order('TRADE_NO',  new Amount(10.2));
$payment->pay($order);
$payment->refund($order,  new Amount(10.2)); //退款

ps:类是我随手写的可能不符合支付宝和wechat实际所需要的,因为我接触的跨境支付比较多,设计到多币种,所以才价格上习惯用Amount对象; 国内的话直接给个数字就可以了,毕竟都是人民币;

也许有人会说了class那么多,抛出来的api必然也很复杂,性能也差,这句话是对的,当开发者使用一款sdk的时候,遇到数组配置的时候很有亲切感,因为写得最多的就是数组,但数组缺乏设计感也是事实,它像是一个黑盒,照着抄把参数扔进去,不用考虑生态; 能够优雅的调用才是优雅的代码。所以包括超哥的wechat,sms, 同样的问题, 简单优雅但还不是足够的优雅。

  • 策略模式

使用策略模式去屏蔽各个渠道的差异性是非常常见的设计方式,但很多sdk都做错了一件事,为了追求对外接口的统一性牺牲部分平台的细节;
当前的项目之前paypal支付一直用的omnipay,优点是接口很简单,付款的时候给个数字完事,但后来由于业务要求需要把用户的订单信息传给paypal做风控,查了很久才发现这个sdk给阉割了因为要维持统一性;无奈换回了官方sdk,非常庞大的sdk,调用起来要写很多代码,但很舒服;
提这个是希望广大的开发者不要真的为了屏蔽细节去阉割官方的功能,interface是必须要实现的但也是可以继承的;100%移植是个基础要求。

以上纯属个人意见,不对的话欢迎指正;ps: 写这么多是因为楼主做了我一直想做的事,无奈一直没机会接触国内的支付,没经验去动工,楼主加油。后面我就写一些海外版支付。:smile:

6年前 评论

赞:thumbsup:

6年前 评论

@dxc1996 感谢支持!欢迎使用,欢迎 Star,欢迎 PR!

6年前 评论

支持!
你把app_id private_key 隐私信息,暴露出来啦!

6年前 评论

@wiladog 没事的哈,因为是沙箱环境的 :smile: 感谢支持!欢迎使用,如有问题,欢迎提 ISSUE!

6年前 评论

几个小建议

  • 同四楼,标题再升华下;
  • 代码的风格,underscore的方式受众还是少于camel
  • 单元测试补上,对于长期维护的项目来说单元测试很重要,不止是规范,还是用来检查别人pr的基础验证
  • composer.lock 文件从版本库中移除
  • 继续抽象 这个得细说明下,和业务无关和代码生态设计有关:

第四点说明

  • 配置问题,下面代码,将不同驱动的写在一个大数组里传给SDK, 这样确实够简单但也是典型缺乏设计的表现
    $config = [
    'alipay' => [
        'app_id' => '2016082000295641',
        'notify_url' => 'http://yansongda.cn/alipay_notify.php',
        'return_url' => 'http://yansongda.cn/return.php',
        'ali_public_key' => '',
        'private_key' => =',
    ],
    ];
    $config_biz = [
    'out_trade_no' => '1',
    'total_amount' => '1',
    'subject' => 'test subject',
    ];
    $pay->pay($config_biz );

如果研究过amazon和paypal登公司sdk的话,你可能会经常遇到一个对象Credential 这个单词执意过来就是“凭据”,sdk和api交互的身份信息;所以如果这么改一下,我会觉得好一点

$credential = new Pay\Alipay\Context\Credential(APP_ID,  ALI_PUBLICK_KEY);
$payment= new  Pay\Alipay\Client($credential);

$payment->purchase(new Product(Currency::RMB, 10.20)); //收款

$order = new Order('TRADE_NO',  new Amount(10.2));
$payment->pay($order);
$payment->refund($order,  new Amount(10.2)); //退款

ps:类是我随手写的可能不符合支付宝和wechat实际所需要的,因为我接触的跨境支付比较多,设计到多币种,所以才价格上习惯用Amount对象; 国内的话直接给个数字就可以了,毕竟都是人民币;

也许有人会说了class那么多,抛出来的api必然也很复杂,性能也差,这句话是对的,当开发者使用一款sdk的时候,遇到数组配置的时候很有亲切感,因为写得最多的就是数组,但数组缺乏设计感也是事实,它像是一个黑盒,照着抄把参数扔进去,不用考虑生态; 能够优雅的调用才是优雅的代码。所以包括超哥的wechat,sms, 同样的问题, 简单优雅但还不是足够的优雅。

  • 策略模式

使用策略模式去屏蔽各个渠道的差异性是非常常见的设计方式,但很多sdk都做错了一件事,为了追求对外接口的统一性牺牲部分平台的细节;
当前的项目之前paypal支付一直用的omnipay,优点是接口很简单,付款的时候给个数字完事,但后来由于业务要求需要把用户的订单信息传给paypal做风控,查了很久才发现这个sdk给阉割了因为要维持统一性;无奈换回了官方sdk,非常庞大的sdk,调用起来要写很多代码,但很舒服;
提这个是希望广大的开发者不要真的为了屏蔽细节去阉割官方的功能,interface是必须要实现的但也是可以继承的;100%移植是个基础要求。

以上纯属个人意见,不对的话欢迎指正;ps: 写这么多是因为楼主做了我一直想做的事,无奈一直没机会接触国内的支付,没经验去动工,楼主加油。后面我就写一些海外版支付。:smile:

6年前 评论

@Tao

非常感谢您的关注与建议。

以下是我的不同意见:

1、关于您说的兼容性问题,不知道您有没有认真阅读 gihub 上的说明文档或者是源代码,但,本 SDK 的兼容性对官方所有功能都是 100% 兼容 的,没有任何阉割,所以请放心。

2、代码风格方面,我秉承的是 Variables 使用 underscore ,method 使用 CamelCase 。我个人认为这样更加易读。所以,我保留我的意见。

3、配置问题方面。我不知道您有没有想过为什么我们这样设计,首先,集成到项目的时候,这些配置项在配置文件中,一般配置过一次就不会再更改了,业务代码中也只需要通过 config() 等辅助函数去或许配置项即可。这样的方法也是大多数的选择,方便快捷易理解,简单优雅。

如果按照您的方法通过 class 去设计的话,我倒觉得有些 过度设计 了,为什么要先到 $credential 再去到 client ?同时,像您说的那样 $credential凭据 的意思,用在 登录,认证等 凭据 统一的情景中较多,在这种单纯调用支付 API 的情况来看,我认为有些 为了去使用而去使用 的意思。所以,我保留我的意见。

但是,您说的 new Order 我倒觉得可以考虑,可行性较高,不过,也有待思考实行方式。

最后,十分期待您的「海外版支付」!

感谢提出宝贵意见!思想的碰撞也能创造出无限的可能!也能使自己理解更深,更加进步!

谢谢!

6年前 评论

@Tao

4、关于 composer.lock 文件的问题,刚刚专门看了 composer 官方文档,看来我没有记错,确实应该上传至版本库中。请传送至 这里 。文档明确表示了需要将 composer.lock 文件上传至版本库中,具体原因请看原文档。

Committing this file to VC is important because it will cause anyone who sets up the project to use the exact same versions of the dependencies that you are using. Your CI server, production machines, other developers in your team, everything and everyone runs on the same dependencies, which mitigates the potential for bugs affecting only some parts of the deployments. Even if you develop alone, in six months when reinstalling the project you can feel confident the dependencies installed are still working even if your dependencies released many new versions since then. (See note below about using the update command.)

感谢支持!

6年前 评论

为什么要先到 $credential 再去到 client ?同时,像您说的那样 $credential 为 凭据 的意思,用在 登录,认证等 凭据 统一的情景中较多

不是,可以想象一下和官方API交互的被称作Client,那么API怎么信任Client的身份呢?那就是Credential;所以不是过度设计,你(Client)拿着通行证(Credential)和我(API)交互,很合理。

配置项在配置文件中,一般配置过一次就不会再更改了,业务代码中也只需要通过 config() 等辅助函数去或许配置项即可

不冲突你依然可以把配置写在配置文件里; 配置和服务是分开的,把服务注册到DI容器的时候读取配置就行

$container->share('payment.alipay', function(){
    $credential = new Credential(config('APPI_ID', config('PUBLIC_KEY')));
     $client = new Payment\Alipay\Client();
    return $client;
});
//需要用到该对象的时候直接从容器获取
$alipay = $container->get('payment.alipay');

ps: 示例代码不是标准的laravel di用法

Variables 使用 underscore ,method 使用 CamelCase 。我个人认为这样更加易读

两个方式没有可读性的差别,习惯问题;psr没有强行要求不过总有惯例,开源项目一般来说都是camel的用法;楼主可以去packagist看看

关于 composer.lock 文件的问题

没仔细看,不过我想那个应该是说使用sdk的项目(project),不是library

6年前 评论

@Tao

感谢关注!

想了想,目前不会使用该方案,至少这个版本不会使用这个方案。理由两点:

  1. API 是通过 APPIDsign 进行认证的,并不是通过 access_token 什么的去认证,而每一次请求的 sign 总是不同的,是个变量

  2. 同时,请求 API 时,也总是需要数组形式传入配置参数,没必要转来转去,还是以前的说法,个人认为有些 为了去使用而去使用

感谢您的指点!受益良多!

谢谢!

6年前 评论

这种项目单元测试还是不能少的,加油

6年前 评论
nff93

已star

顺便说一句,@Tao 说的有一些问题确实值得关注,比如单元测试、不同支付平台独有功能等。

6年前 评论

@nff93

是的,感谢您的关注,也感谢 @Tao 的建议。

目前计划:

  1. 单元测试已经开始着手;
  2. composer.lock 文件已经从版本库中移除,是我孤陋寡闻了,感谢@Tao 的指点;
  3. 下一个大版本将抽象 订单类;

同时,再次声明,和官方功能 兼容性100%,不同支付平台独有功能照常可以使用。

6年前 评论

?
另外 omnipay 这个包做支付也很不错哟~

6年前 评论

感谢分享...学习了

6年前 评论

@yansongda 今天使用了一下,返现同步验证签名跟异步签名,总是失败.于是我下载了官方的sdk 同样的公钥 秘钥 ,官方的sdk签名就可以通过,laravel里面的不行.....这其中有什么不一样的嘛???????好纠结.

6年前 评论

@736713830 请问您能否将您的代码及配置文件贴出来?感谢支持

6年前 评论

@yansongda 恩恩感谢!

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Yansongda\Pay\Pay;
use Log;

class PayController extends Controller
{
    public $config = [
        'alipay' => [
            'app_id'         => '2016082000299388',
            'notify_url'     => "http://zhihu.dev/pay/notify/",
            'return_url'     => "http://zhihu.dev/pay/return/",
            'private_key'    => 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDrOYOItwkL1MstW8yIqSamjiNlPpIwsf8wE21cHoE+lYA8soH6et1C8BNr3ur6+Y85uOdAWK9Bq4iCXN7O0gCfiC7N37L+ecfVX7nmxaUx1kQb7j3o6qGc+j2IH/NwGb5yjpl2/1R2PDB2cFfjXsbzKXKkPz51xKS1qN5kE9OWLwpHkO93KNBpQ9KfIorEioRswqm64hQKrKMVGXVNCQV6bFKWGEi/GT+tNL6x8INAPsn+8Sr4aAAy1zmWH8nVJlCg0JRLag9z/d7J2WWa/1kd5xrYeYhSgkfs4KfNkeKxMWBa97oN/9oQaHo0wWXZDXLj4DwQdh+YHjsK7Xxg3sqDAgMBAAECggEBALOnO6TBVvFoVr2P7Uk7F8ABs1ryTSlP6T1IuS+wRLoN6OFy3P73s+IaBltO6F1DGXbWmBh3I3OelSYr1ChWEA/ILSckAUuGq292Kz/sGF1V7rL+ZN5txX1lSnWbIYyuvNw1uVQ/crzS/5iV+So5Cu5Q67QycXv08m+kSCZbVjDOycw95bJdgwf6aUnutq9e84LxVfUNtQcmbvAwMm/EHu959aYmg0/X4XwFt9AlzGaG7VNZ9PrXh+3I3szudIlWIqCE4x5BPYNrNgTG8MDgc2M44vguuppLc9dIyxMLfuUs5TFlw26H+rdrN/svSuAKcNNIldxcpJJ9ON4cd41Ft+ECgYEA9m/zANRiBRDM+Gb5Rve6PRKv89Z3KKO3Ea1mIfgaZYmTqwdQZRYlsdH9jAF5TW8vIvmrzYn5G2Wzel2ByXAoNjlS5Uk65HCVsfW9icu+tvSbZFPQv+PksL+BT6+XZpAQdbA+2gWFankMtPGlI3e8B0obbtBSz6MPu7J1RFdVtysCgYEA9FouSy+iHcsk1yYgph7M7ekRy6hQZTwfhtXcSg3mQP9rAS7qC+i5Cahm+nDlC8DnIEXujNanQvZbtOF1yLWKytcSI2XfsLPNMFuLpJ12RdDlT1fwm0v1qT9+sbFf75nCeyjZue3DZIQwk6ZQgiva1OYe5HxiRui4uIIlUKEFDgkCgYAnWPiTyVVdObGAd/CVRDiYR8OQS037TyiLygPkqJiOXckWJbTQbxjVq5GwKQwVMQt1qiekScNcfICLqSJv51iET+LMeUTN9KvyHiqFNeLdk3C8NCXAfcKl9e3Pq1mhJp96KwneBcOnnGxK98pGP3fmMg7vQpP2UzLP5U6T+t3UwQKBgCe6DbK9oM7VOz0bfs1jTpATh4gj6kY7Oyjx1inYDqpcnGoJ1ZVwFb1jOrLW7fY2O518oScWswTpBsnmqf6ivVjzFIjaxD53TRHioFZ4H0WEi/OVaZHmDJ+iqhd7rgxHZF4fqx5WcKED+N3YeVhghuh8LWOacWPz7CxbRHPSaf75AoGBAMCotPv3jVABDW3p0kt9eyulfunmkD4qL2q4DP8SwzxVq4WwwXuE20U6DSGYUijsI7utmhVJxE6FkFKR9NvadtZ/KBd5O/eLbF2ORSqdzcEmvksuJyvs2vfl0c/X7j+I/9gzBgCB6e9oPzdZafe6bqy7xIsY8lSnCb6tfDVRM5N6',
            'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6zmDiLcJC9TLLVvMiKkmpo4jZT6SMLH/MBNtXB6BPpWAPLKB+nrdQvATa97q+vmPObjnQFivQauIglzeztIAn4guzd+y/nnH1V+55sWlMdZEG+496OqhnPo9iB/zcBm+co6Zdv9UdjwwdnBX417G8ylypD8+dcSktajeZBPTli8KR5DvdyjQaUPSnyKKxIqEbMKpuuIUCqyjFRl1TQkFemxSlhhIvxk/rTS+sfCDQD7J/vEq+GgAMtc5lh/J1SZQoNCUS2oPc/3eydllmv9ZHeca2HmIUoJH7OCnzZHisTFgWve6Df/aEGh6NMFl2Q1y4+A8EHYfmB47Cu18YN7KgwIDAQAB',
        ],
    ];

    //发起支付
    public function index()
    {
        $config_biz = [
            'out_trade_no' => time(),
            'total_amount' => '1',
            'subject'      => 'test subject',
        ];
        $pay = new Pay($this->config);
        return $pay->driver('alipay')->gateway('web')->pay($config_biz);

    }

    //同步通知
    public function results()
    {
        //dump($_GET);
        $pay = new Pay($this->config);
        $res = $pay->driver('alipay')->gateway('web')->verify($_GET, null, false);
        var_dump($res);//返回false
    }

    //异步通知
    public function notify(Request $request)
    {
        $pay = new Pay($this->config);
        $res = $pay->driver('alipay')->gateway('web')->verify($request->all(), null, true);
        var_dump($res);//返回false
    }
}
6年前 评论

@736713830 网关是用的https://openapi.alipaydev.com/gateway.do 沙盒环境的.

6年前 评论

@yansongda 网关是用的https://openapi.alipaydev.com/gateway.do 沙盒环境的.

file

6年前 评论

@736713830

您好。

抱歉给您带来不好的体验,不过,我刚刚测试了一下,没有任何问题出现,无论是同步验证还是异步验证。测试代码如下:

1、安装

composer require yansongda/laravel-pay

注意,以上 laravel 包只是对 yansongda/pay 这个包进行了封装,实际还是使用的yansongda/pay 这个包。

2、编辑 controller

<?php

namespace App\Http\Controllers;

use Pay;
use Illuminate\Http\Request;

class PayController extends Controller
{
    public function index()
    {
        $config_biz = [
            'out_trade_no' => time(),
            'total_amount' => '1',
            'subject'      => 'test subject',
        ];

        return Pay::driver('alipay')->gateway()->pay($config_biz);
    }

    public function return(Request $request)
    {
        return Pay::driver('alipay')->gateway()->verify($request->all());
    }

    public function notify(Request $request)
    {
        if (Pay::driver('alipay')->gateway()->verify($request->all())) {
            file_put_contents(storage_path('notify.txt'), "收到来自支付宝的异步通知\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单号:' . $request->out_trade_no . "\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单金额:' . $request->total_amount . "\r\n\r\n", FILE_APPEND);
        } else {
            file_put_contents(storage_path('notify.txt'), "收到异步通知\r\n", FILE_APPEND);
        }

        echo "success";
    }
}

3、编辑路由

<?php

Route::get('/', 'PayController@index');

Route::get('return', 'PayController@return');

Route::post('notify', 'PayController@notify');

注意,这里由于 laravel 会对 post 数据进行 csrf 验证,所以,在 VerifyCsrfToken 中间件中加入以下代码

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'notify*'
    ];
}

4、测试结果

访问 yansongda.cn 进行支付测试,测试完成后,同步结果截图如下:
return

异步通知如下:
notify

结论

目前测试来看,没有任何问题,还请您根据以上代码认真检查下您的代码。

说明:
verify() 方法不需要传入第三个参数的

verify($data, $sign = null)
说明:验证服务器返回消息是否合法
参数:$data 为服务器接收到的原始内容,$sign 为签名信息,当其为空时,系统将自动转化 $data 为数组,然后取$data['sign']。
返回:mixed 验证成功,返回 服务器返回的数组;否则返回 false;

感谢您的支持。

6年前 评论

@736713830

我的配置文件如下:

<?php

return [
    'alipay' => [
        // 支付宝分配的 APPID
        'app_id' => '2016082000295641',

        // 支付宝异步通知地址
        'notify_url' => 'http://yansongda.cn/notify',

        // 支付成功后同步通知地址
        'return_url' => 'http://yansongda.cn/return',

        // 阿里公共密钥,验证签名时使用
        'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB',

        // 自己的私钥,签名时使用
        'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==',
    ],

    'wechat' => [
        // 公众号APPID
        'app_id' => '',

        // 小程序APPID
        'miniapp_id' => '',

        // APP 引用的 appid
        'appid' => '',

        // 微信支付分配的微信商户号
        'mch_id' => '',

        // 微信支付异步通知地址
        'notify_url' => '',

        // 微信支付签名秘钥
        'key' => '',

        // 客户端证书路径,退款时需要用到。请填写绝对路径,linux 请确保权限问题。pem 格式。
        'cert_client' => '',

        // 客户端秘钥路径,退款时需要用到。请填写绝对路径,linux 请确保权限问题。pem 格式。
        'cert_key' => '',
    ],
];
6年前 评论

@yansongda 感谢.我在好好看看.多谢.

6年前 评论

@yansongda 我试着用的app_id 发现用你的可以...........同步异步都可以.....用自己的app_id就不行,但sdk可以.....无语了..

6年前 评论

@yansongda

file 秘钥是这么设置么?把商户公钥写入 rsa2这个里面???

6年前 评论

@yansongda 再次感谢作者能在百忙之中指点一二,折腾了半天终于搞明白问题出现在那里了.一直以为工具生成公钥直接写在

file这里面就行.然后公钥私钥都从工具粘贴到配置项里.......其实公钥得到

file这里面查看...哎.......搞得半天签名都不通过.......搞混了...啊哈哈哈哈

6年前 评论

@736713830 感谢支持,已经重新更新修改了文档,细化了使用方法。

6年前 评论

很好,支付宝现在存在的大多数都是老板即时到帐接口,用新版的不多

6年前 评论

@yansongda 想弄一下微信测试,,,没有公共号去哪里能搞到 测试账号????????????????

6年前 评论

@736713830 这个暂时没有方法哦。

6年前 评论

@736713830 没有测试帐号,但是可以先申请,再用沙箱

6年前 评论
Jourdon

正好最近要做支付,感谢分享

6年前 评论

支付宝公钥是需要上传到支付宝后台获取的,之前我也坑过

6年前 评论

刚测试了下封装好的laravel包,支付宝notify_url和return_url是全局配置的,微信端的是可以下单时候改配置。支付宝部分可以优化下

6年前 评论

感谢分享!!

6年前 评论

@ab0029 一般情况下,异步通知或同步通知都是固定的某个链接,不知道单独配置使用场景在哪里?

6年前 评论

@yansongda 多业务下通知地址一般是分开的,例如充值类型支付一个通知地址,普通购物支付一个通知地址,等等...虽然都可以集于一个通知地址维护,但是分开可以有效地控制不同业务

6年前 评论

@未寒 申请??? 你说的申请指的是认证开发者么???????????????????

6年前 评论
shizhice

@yansongda 不错不错。做支付(alipay,wechat)一年多,深受官方sdk的煎熬。

6年前 评论

@736713830 先申请微信支付。。。

6年前 评论

@未寒 再具体点....

6年前 评论

@未寒 首先是我没有公众账号啊 这怎么弄??? 具体点 在线等.

6年前 评论
shizhice

Yansongda\Pay\Gateways\Alipay\Alipay文件219行,当subject为中文时,会导致下单失败。去掉JSON_UNESCAPED_UNICODE参数后,问题解决。

file

6年前 评论

@骑着毛驴去东莞 只有使用 刷卡和扫码 两种支付方法时才会调用这个函数,请问您是在这两种支付方式中下单失败吗?

6年前 评论

@骑着毛驴去东莞 还有请问下报错信息是什么呢?

6年前 评论
shizhice

@yansongda
我刚完成扫码功能,发现这个问题。刷卡,目前没有需求。这个是报错信息

Yansongda\Pay\Exceptions\GatewayException: get result error: - 

我测试了您的代码,阿里返回的错误代码如下

{"code":"40002","msg":"Invalid Arguments","sub_code":"isv.invalid-signature",...}

具体内容,不方便截图,请见谅

6年前 评论

必须要顶一下,顺便来个star

6年前 评论

@骑着毛驴去东莞 好吧是monokai 我色盲了,以为是类似Atom的配色

6年前 评论

支付成功,返回回调的时候出现:The Response content must be a string or object implementing __toString(), "boolean" given.怎么回事?

6年前 评论

@tianxionglu 麻烦请 github 上提 issue,同时,麻烦带上代码、错误信息等必要信息,不然很难判断。

感谢您的支持!

6年前 评论

@yansongda 商品名称中含有&符号,导致验签失败,请问该怎么处理?

6年前 评论

@ShiYue 这个没得处理,用其它符号代替吧。微信支付宝官方文档中有说明的。

6年前 评论

@yansongda @yansongda 支付宝异步回调验签失败,已检查公钥设置,打印 回调验签信息,发现中文变成了乱码,是不是这个原因引起验签失败??
我写的代码部分

 if (Pay::driver('alipay')->gateway()->verify($request->all())) {
            if ($request->has('trade_status')) {
                switch ($request->trade_status) {
                    case 'TRADE_SUCCESS':
                    case 'TRADE_FINISHED':
                        $orders = Orders::query()->where('number', $request->out_trade_no)->first();
                        if ($orders) {
                            $user = User::query()->findOrFail($orders->user_id);
                            if ($orders->payment_total == $request->total_amount) {
                                if ($orders->payment_state == 0) {
                                    $user->increment('integral', $orders->payment_total);
                                    event(new integral($user, '消费, ' . '订单号' . $orders->number, '+ ' . $orders->payment_total));
                                    $orders->payment_state = 1;
                                    $orders->payment_method = 1;
                                    $orders->state = 1;
                                    event(new LessStock($orders));
                                    $orders->save();
                                }
                            }
                        }
                        break;
                }
                return 'success';
            }
        } else {
            return 'fail';
        }

支付宝回调的部分信息

"subject":"Art Life �~X��~I��~N� �~T��~^~K","sign":"m/jE++9t
6年前 评论

@ShiYue 无论中文应该不影响的。麻烦 github 提 issue 吧。

6年前 评论

@了然

请自行检查下秘钥文件是否存在,文件权限、文件格式是否为标准 RSA 格式。如有问题请 github 上提 ISSUE。

感谢支持

6年前 评论

请问一下,微信沙箱测试如何获取秘钥

6年前 评论

请问lumen能使用此sdk不

5年前 评论

@yansongda 感谢 这个sdk给我带来很多便利 :+1:

5年前 评论

支持,可惜没有集成银联的进去哇。。。

5年前 评论

楼主,希望支持银联,upup

5年前 评论

无意冒犯,但是想说:easywechat好像已经很成熟了,为什么还要选择重复造轮子呢?不太理解。

5年前 评论

@nligo easywechat 没有 Alipay , 当你需要同时支持这两家时你就理解了

5年前 评论

大哥,有微信或QQ吗 ,我想加下您

5年前 评论

这里面好像还缺少一部分,支付宝目前是分新平台和老平台的,互相不通用,看文档这里面实现的是新平台的,很多以前申请的接口都是老平台的,希望加上。
另外支付宝对于公益行业有一个免手续费的网络捐赠接口,虽然比较小众,但是我找了很久,发现没有一个包支持这功能的,希望能加上。

4年前 评论

请问微信扫码支付是只生成链接吗,二维码是不是还要自己处理。

4年前 评论

1。前后端分离,支付宝支付的同步回调地址怎么填,填了前端地址的话前端如何验证呢

  1. 微信退款的证书放在前端目录下还是后端目录下
4年前 评论

不行啊 driver什么意思报错了

3年前 评论

支付宝发起请求提示 "错误代码 invalid-signature 错误原因: 验签出错,请确认charset参数放在了URL查询字符串中且各参数值使用charset参数指示的字符集编码" 。然后我设置了charset = utf-8 还是这个错误。支付宝公钥和应用私钥换了好几次还是提示这个错误 :sob:

3年前 评论

弱弱问下,omnipay 和你这个都没支持第三方 API 吧,就是子商户

2年前 评论

AlipayOpenAgentCreateRequest 找了,没有,我用官方的看看

2年前 评论

为什么我的微信支付配置好后总是获取不到证书,提示“Get Wechat Public Cert Error”?

2年前 评论

看了文档 新版的配置的配置证书的,但是支付宝工具生成的证书是txt?请教下大佬要怎么转? 用的是公钥模式

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!