PHP支付宝支付流程

wen PHP项目 2

本文目录导读:

PHP支付宝支付流程

  1. 支付流程概述
  2. 环境准备
  3. 完整实现代码
  4. 安全注意事项
  5. 数据库表结构
  6. 测试环境配置
  7. 常见问题处理
  8. 最佳实践

我来为你详细讲解支付宝支付在PHP中的完整流程。

支付流程概述

基本流程图

用户下单 → 生成订单 → 请求支付宝 → 支付宝处理 → 同步返回 → 异步通知 → 查询验证

环境准备

安装SDK

composer require alipay/alipay-sdk-php

配置文件

// config/alipay.php
return [
    'app_id' => '你的应用ID',
    'private_key' => '你的应用私钥',
    'alipay_public_key' => '支付宝公钥',
    'notify_url' => 'https://your-domain.com/pay/notify',
    'return_url' => 'https://your-domain.com/pay/return',
    'sign_type' => 'RSA2',
    'gateway' => 'https://openapi.alipay.com/gateway.do',
    'charset' => 'UTF-8'
];

完整实现代码

支付控制器类

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Config;
class AlipayController extends Controller
{
    protected $alipayConfig;
    public function __construct()
    {
        $this->alipayConfig = config('alipay');
        $this->initAlipay();
    }
    /**
     * 初始化支付宝
     */
    private function initAlipay()
    {
        $config = new Config();
        $config->protocol = 'https';
        $config->gatewayHost = 'openapi.alipay.com';
        $config->signType = $this->alipayConfig['sign_type'];
        $config->appId = $this->alipayConfig['app_id'];
        $config->merchantPrivateKey = $this->alipayConfig['private_key'];
        $config->alipayPublicKey = $this->alipayConfig['alipay_public_key'];
        Factory::setOptions($config);
    }
    /**
     * 生成支付二维码(PC端)
     */
    public function pagePay(Request $request)
    {
        $orderId = $request->input('order_id');
        $amount = $request->input('amount');
        $subject = $request->input('subject');
        try {
            // 创建支付请求
            $result = Factory::payment()->page()->pay(
                $subject,           // 订单标题
                $orderId,           // 商户订单号
                $amount,            // 订单金额
                $this->alipayConfig['notify_url'],  // 异步通知地址
                $this->alipayConfig['return_url']   // 同步跳转地址
            );
            // 跳转到支付宝页面
            return redirect($result->body);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => '创建支付失败: ' . $e->getMessage()
            ]);
        }
    }
    /**
     * APP支付
     */
    public function appPay(Request $request)
    {
        $orderId = $request->input('order_id');
        $amount = $request->input('amount');
        $subject = $request->input('subject');
        try {
            // 创建APP支付
            $result = Factory::payment()->app()->pay(
                $subject,
                $orderId,
                $amount,
                $this->alipayConfig['notify_url']
            );
            // 返回给客户端调用SDK拉起支付
            return response()->json([
                'code' => 0,
                'data' => [
                    'order_str' => $result->body
                ],
                'message' => 'success'
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => '创建支付失败: ' . $e->getMessage()
            ]);
        }
    }
}

异步通知处理

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class AlipayNotifyController extends Controller
{
    /**
     * 支付宝异步通知
     */
    public function notify(Request $request)
    {
        $params = $request->all();
        // 日志记录,方便调试
        Log::info('支付宝异步通知', $params);
        // 验证签名和处理业务
        $result = app('alipay')->verify($params);
        if ($result) {
            // 检查商户订单号
            $orderId = $params['out_trade_no'];
            $tradeNo = $params['trade_no'];
            $tradeStatus = $params['trade_status'];
            // 处理业务逻辑
            $order = Order::where('order_no', $orderId)->first();
            if ($order && $order->status == 'pending') {
                if ($tradeStatus == 'TRADE_SUCCESS' || $tradeStatus == 'TRADE_FINISHED') {
                    // 验证金额
                    if (bccomp($order->amount, $params['total_amount'], 2) === 0) {
                        // 更新订单状态
                        $order->status = 'paid';
                        $order->transaction_id = $tradeNo;
                        $order->paid_at = now();
                        $order->save();
                        // 执行后续业务逻辑(如发货、增加会员等)
                        $this->afterPaymentSuccess($order);
                    }
                }
            }
            // 只有返回"success"才表示处理成功
            return 'success';
        }
        return 'fail';
    }
    /**
     * 支付成功后的业务处理
     */
    private function afterPaymentSuccess($order)
    {
        // 更新库存
        // 发送邮件通知
        // 生成发票等
        // 示例:记录支付日志
        PaymentLog::create([
            'order_id' => $order->id,
            'transaction_id' => $order->transaction_id,
            'amount' => $order->amount,
            'status' => 'success',
            'paid_at' => now()
        ]);
    }
}

同步返回处理

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AlipayReturnController extends Controller
{
    /**
     * 支付完成后的页面跳转
     */
    public function return(Request $request)
    {
        $params = $request->all();
        // 验证签名
        $result = app('alipay')->verify($params);
        if (!$result) {
            return redirect('/order/fail')->with('error', '支付验证失败');
        }
        $orderId = $params['out_trade_no'];
        $tradeStatus = $params['trade_status'];
        // 获取订单信息
        $order = Order::where('order_no', $orderId)->first();
        // 查询订单状态
        if ($tradeStatus == 'TRADE_SUCCESS') {
            // 订单支付成功
            return redirect('/order/success')->with([
                'order_no' => $orderId,
                'trade_no' => $params['trade_no']
            ]);
        }
        return redirect('/order/fail');
    }
}

订单查询

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Alipay\EasySDK\Kernel\Factory;
class OrderQueryController extends Controller
{
    /**
     * 主动查询订单状态
     */
    public function queryOrder(Request $request)
    {
        $orderId = $request->input('order_no');
        try {
            $result = Factory::payment()->common()->query($orderId);
            if ($result->tradeStatus == 'TRADE_SUCCESS') {
                // 支付成功,处理业务逻辑
                return response()->json([
                    'code' => 0,
                    'data' => [
                        'order_no' => $result->outTradeNo,
                        'trade_no' => $result->tradeNo,
                        'total_amount' => $result->totalAmount,
                        'status' => 'success'
                    ]
                ]);
            }
            return response()->json([
                'code' => 1,
                'message' => '订单未支付'
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => '查询失败: ' . $e->getMessage()
            ]);
        }
    }
}

退款功能

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Alipay\EasySDK\Kernel\Factory;
class RefundController extends Controller
{
    /**
     * 发起退款
     */
    public function refund(Request $request)
    {
        $orderId = $request->input('order_no');
        $refundAmount = $request->input('refund_amount');
        $refundReason = $request->input('refund_reason', '用户申请退款');
        // 生成退款单号
        $refundNo = 'REFUND_' . date('YmdHis') . rand(1000, 9999);
        try {
            $result = Factory::payment()->common()->refund(
                $orderId,           // 商户订单号
                $refundNo,          // 退款单号
                $refundAmount,      // 退款金额
                $refundReason       // 退款原因
            );
            // 更新退款信息
            if ($result->code == '10000') {
                RefundRecord::create([
                    'order_no' => $orderId,
                    'refund_no' => $refundNo,
                    'refund_amount' => $refundAmount,
                    'status' => 'success',
                    'refund_time' => now()
                ]);
                return response()->json([
                    'code' => 0,
                    'message' => '退款成功',
                    'data' => [
                        'refund_no' => $refundNo
                    ]
                ]);
            }
        } catch (\Exception $e) {
            // 处理退款异常
            Log::error('退款失败', [
                'order_no' => $orderId,
                'refund_no' => $refundNo,
                'error' => $e->getMessage()
            ]);
            return response()->json([
                'code' => 500,
                'message' => '退款失败: ' . $e->getMessage()
            ]);
        }
    }
}

前台订单控制器

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class OrderController extends Controller
{
    /**
     * 下单并支付
     */
    public function createAndPay(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'product_id' => 'required|exists:products,id',
            'quantity' => 'required|integer|min:1'
        ]);
        if ($validator->fails()) {
            return response()->json([
                'code' => 400,
                'message' => $validator->errors()->first()
            ]);
        }
        // 创建订单
        $product = Product::find($request->product_id);
        $order = Order::create([
            'order_no' => 'ORDER_' . date('YmdHis') . rand(1000, 9999),
            'product_id' => $product->id,
            'quantity' => $request->quantity,
            'amount' => $product->price * $request->quantity,
            'status' => 'pending',
            'created_at' => now()
        ]);
        // 调用支付宝支付
        $payController = new AlipayController();
        // 如果是PC端
        return app(AlipayController::class)->pagePay(new Request([
            'order_id' => $order->order_no,
            'amount' => $order->amount,
            'subject' => $product->name
        ]));
    }
}

路由配置

// routes/web.php
Route::post('/pay/alipay/page', 'AlipayController@pagePay')->name('pay.alipay.page');
Route::post('/pay/alipay/app', 'AlipayController@appPay')->name('pay.alipay.app');
Route::post('/pay/alipay/notify', 'AlipayNotifyController@notify')->name('pay.alipay.notify');
Route::get('/pay/alipay/return', 'AlipayReturnController@return')->name('pay.alipay.return');
Route::post('/order/query', 'OrderQueryController@queryOrder')->name('order.query');
Route::post('/order/refund', 'RefundController@refund')->name('order.refund');
// routes/api.php (APP使用)
Route::post('/pay/alipay/app-pay', 'AlipayController@appPay');

安全注意事项

签名验证

/**
 * 安全验证
 */
private function verifyPay($params)
{
    try {
        // 验证签名
        $result = app('alipay')->verify($params);
        if (!$result) {
            Log::warning('支付宝验证失败', $params);
            return false;
        }
        // 检查应用ID是否匹配
        if ($params['app_id'] != config('alipay.app_id')) {
            Log::error('应用ID不匹配');
            return false;
        }
        // 检查商户订单号是否存在
        $order = Order::where('order_no', $params['out_trade_no'])->first();
        if (!$order) {
            Log::error('订单不存在: ' . $params['out_trade_no']);
            return false;
        }
        // 检查金额是否一致
        if (bccomp($order->amount, $params['total_amount'], 2) !== 0) {
            Log::error('金额不匹配', [
                'order_amount' => $order->amount,
                'paid_amount' => $params['total_amount']
            ]);
            return false;
        }
        // 检查是否处理过该订单
        if ($order->status == 'paid') {
            Log::info('订单已处理');
            return ['status' => 'processed'];
        }
        return [
            'status' => 'valid',
            'order' => $order,
            'params' => $params
        ];
    } catch (\Exception $e) {
        Log::error('支付验证异常: ' . $e->getMessage());
        return false;
    }
}

防护措施

/**
 * 防止重复通知
 */
private function preventDuplicateNotification($orderId)
{
    // 使用缓存或数据库唯一索引
    $key = 'payment_notify_' . $orderId;
    if (Cache::has($key)) {
        return false; // 已处理过
    }
    // 设置短期缓存
    Cache::put($key, true, 600); // 10分钟有效
    return true;
}

数据库表结构

-- 订单表
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_no VARCHAR(64) NOT NULL UNIQUE,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    status ENUM('pending', 'paid', 'refunded') DEFAULT 'pending',
    transaction_id VARCHAR(64),
    paid_at DATETIME,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_order_status (status)
);
-- 支付日志表
CREATE TABLE payment_logs (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    transaction_id VARCHAR(64),
    amount DECIMAL(10,2),
    status VARCHAR(32),
    raw_notify_data TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

测试环境配置

// 沙箱环境
$config->gatewayHost = 'openapi.alipaydev.com';
// 测试账号
// 商户APPID: 9021000122xxxxxx
// 商户私钥: MIIEvQIBADANB... 
// 支付宝公钥: MIIBIjANBgkqh...

常见问题处理

签名验证失败

// 检查密钥格式
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . 
              chunk_split($config['private_key'], 64, "\n") . 
              "-----END RSA PRIVATE KEY-----";

金额精度

// 使用高精度计算
$amount = number_format($amount, 2, '.', '');

通知处理超时

// 设置较短的超时时间,快速响应支付宝
set_time_limit(30);
// 先返回成功,异步处理业务
return response()->json([
    'code' => 0,
    'message' => 'success'
]);

最佳实践

  1. 测试环境使用沙箱:开发阶段使用支付宝沙箱环境
  2. 完善日志:记录所有支付相关请求和响应
  3. 异步处理:通知处理尽量异步化
  4. 幂等性:确保同一订单不会重复处理
  5. 金额验证:严格验证支付金额与订单金额一致
  6. 事务处理:订单状态更新使用数据库事务

这个流程涵盖了从发起支付到异步通知的完整过程,建议在开发时先在沙箱环境测试完整流程,确认无误后再切换到生产环境。

抱歉,评论功能暂时关闭!