• PHP使用 yansongda/pay 实现支付宝-网页支付功能,转账提现功能


    PHP使用 yansongda/pay 实现支付宝-网页支付功能,转账提现功能

    官方文档

    https://pay.yansongda.cn/

    安装

    注意PHP对应版本
    composer require yansongda/pay

    代码

    这里的获取配置方式跟文档有些差别,因为证书地址需要动态配置获取存在一些问题

    
    /**
     * 支付宝网关
     */
    
    use App\Exceptions\ApiException;
    use Illuminate\Container\Container;
    use Psr\Http\Message\ResponseInterface;
    use Yansongda\Pay\Exception\ContainerException;
    use Yansongda\Pay\Exception\InvalidParamsException;
    use Yansongda\Pay\Pay;
    use Yansongda\Pay\Plugin\Alipay\Fund\AccountQueryPlugin;
    
    class AlipayGateway
    {
        /**
         * @return array
         */
        public function getConfig(): array
        {
            return [
                'alipay' => [
                    'default' => [
                        // 必填-支付宝分配的 app_id
                        'app_id' => 'xxx',
                        // 必填-应用私钥 字符串或路径
                        // 在 https://open.alipay.com/develop/manage 《应用详情->开发设置->接口加签方式》中设置
                        'app_secret_cert' => 'xxx',
                        // 必填-应用公钥证书 路径
                        // 设置应用私钥后,即可下载得到以下3个证书
                        'app_public_cert_path' => storage_path('app/certificate/alipay/appCertPublicKey_2021004115668591.crt'),
                        // 必填-支付宝公钥证书 路径
                        'alipay_public_cert_path' => storage_path('app/certificate/alipay/alipayCertPublicKey_RSA2.crt'),
                        // 必填-支付宝根证书 路径
                        'alipay_root_cert_path' => storage_path('app/certificate/alipay/alipayRootCert.crt'),
                        // 回调地址
                        'return_url' => 'https://xxx.cn/api/pay/alipay/return',
                        // 通知地址
                        'notify_url' => 'https://xxx.cn/api/pay/alipay/notify',
                        // 选填-第三方应用授权token
                        'app_auth_token' => '',
                        // 选填-服务商模式下的服务商 id,当 mode 为 Pay::MODE_SERVICE 时使用该参数
                        'service_provider_id' => '',
                        // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE
                        'mode' => Pay::MODE_NORMAL,
                    ]
                ],
            ];
        }
    
        /**
         * 创建交易(Web端)
         */
        public function web($orderNo, $totalAmount, $subject): string
        {
            $result = Pay::alipay($this->getConfig())->web([
                'out_trade_no' => $orderNo,
                'total_amount' => $totalAmount,
                'subject' => $subject,
            ]);
            return $result->getBody();
        }
    
        /**
         * 转账/提现
         * @param string $orderNo 订单编号
         * @param string $totalAmount 总金额
         * @param string $payeeIdentity OpenID
         * @return array
         * @throws ApiException
         */
        public function transfer(string $orderNo, string $totalAmount, string $payeeIdentity): array
        {
            $container = Container::getInstance();
            $response = Pay::alipay($this->getConfig(), $container)->transfer([
                'out_biz_no' => $orderNo,
                'trans_amount' => $totalAmount,
                'product_code' => 'TRANS_ACCOUNT_NO_PWD',
                'biz_scene' => 'DIRECT_TRANSFER',
                'payee_info' => [
                    'identity' => $payeeIdentity,
                    'identity_type' => 'ALIPAY_OPEN_ID',
    //                'name' => '沙箱环境'
                ],
            ]);
            // 转换获取数组结果
            $result = $response->toArray();
            if ($this->success($result)) {
                return $result;
            }
            logger('transfer->', $result);
            throw  new ApiException(1001, $result['sub_msg']);
        }
    
        /**
         * 账户查询余额(商家)
         * @throws ApiException
         */
        public function accountQueryBalance()
        {
            try {
                $params = [
                    'alipay_user_id' => 'xxx', // 支付宝会员ID (这里填写商家号)
                    'account_type' => 'ACCTRANS_ACCOUNT', // 查询的账号类型
                ];
                $allPlugins = Pay::alipay($this->getConfig())->mergeCommonPlugins([AccountQueryPlugin::class]);
                $response = Pay::alipay($this->getConfig())->pay($allPlugins, $params);
                $result = $response->toArray();
                if ($this->success($result)) {
                    return $result['available_amount'];
                } else {
                    throw new ApiException(1001, '账户查询余额错误');
                }
            } catch (ContainerException | InvalidParamsException $e) {
                throw new ApiException(1001, '账户查询余额->错误码: ' . $e->getCode() . '提示: ' . $e->getMessage());
            }
        }
    
        /**
         * 回调处理
         * @throws InvalidParamsException
         * @throws ContainerException
         */
        public function returnCallback(): array
        {
            $data = Pay::alipay($this->getConfig())->callback(); // 是的,验签就这么简单!
            // 信息处理
            // 订单号:$data->out_trade_no
            // 支付宝交易号:$data->trade_no
            // 订单总金额:$data->total_amount
            return $data->toArray();
        }
    
        /**
         * 通知处理
         * @return ResponseInterface
         */
        public function notifyCallback(): ResponseInterface
        {
            $alipay = Pay::alipay($this->getConfig());
            try {
                $data = $alipay->callback(); // 是的,验签就这么简单!
                logger('alipay->notifyCallback', $data->toArray());
                // 订单逻辑处理
                // 请自行对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
                // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号;
                // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额);
                // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email);
                // 4、验证app_id是否为该商户本身。
                // 5、其它业务逻辑情况
            } catch (\Exception $e) {
                // $e->getMessage();
                logger('alipay->notifyCallback:错误提示' . $e->getMessage());
            }
            return $alipay->success();
        }
    
        /**
         * 判断结果是否成功
         * @param array $response
         * @return bool
         */
        public function success(array $response): bool
        {
            if (!empty($response['code']) && $response['code'] == 10000) {
                return true;
            }
            if (empty($response['code']) && empty($response['sub_code'])) {
                return true;
            }
            return false;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175

    调用示例

    /**
         * 支付示例-网页重定向付款
         */
        public function pay(Request $request): string
        {
            // 订单号
            $orderNo = '001' . time();
            // 金额
            $totalAmount = '0.1';
            $alipay = new AlipayGateway();
            // 网页重定向付款
            $result = $alipay->web($orderNo, $totalAmount, '商品001');
            return view('pay', ['data' => $result]);
        }
    
        /**
         * 支付示例-提现/转账
         * @throws ApiException
         */
        public function transfer(Request $request): array
        {
            // 订单号
            $orderNo = '001' . time();
            // 金额(最小金额 0.1)
            $totalAmount = '0.1';
            $alipay = new AlipayGateway();
            $balance = $alipay->accountQueryBalance();
            // 余额相等或者 余额大于总金额 则可以转账
            if (bccomp($balance, $totalAmount, 1) !== -1) {
                // 转账 (自行获取OpenId)
                $payeeIdentity = 'xxx'; // openid
                return $alipay->transfer($orderNo, $totalAmount, $payeeIdentity);
            }
            return [
                'msg' => '商户账户余额不足, 请联系商家'
            ];
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    注意1: web 方法需要页面承载返回结果的 form 代码
    注意2: OpenId 需要使用支付宝登录获取

  • 相关阅读:
    【MySQL学习】C++外部调用
    Android相机调用-libusbCamera【外接摄像头】【USB摄像头】 【多摄像头预览】
    P09 约束
    如何设置让vs 在生成程序错误的情况下不去执行上一个可以执行的程序?
    Mac book pro 睡眠唤醒之后,外接显示器再也无法点亮,只能重启,怎么解决?
    互联网名词解释
    Flutter Android 热修复方案(3.22.0)
    并发支持库(2)-原子操作
    Python进阶教学——多线程高级应用
    元素可视区client系列和元素滚动 scroll 系列
  • 原文地址:https://blog.csdn.net/weixin_41258075/article/details/133159978