返回首页

支付接口开发

支付插件是 DCSHOP 的核心扩展之一,用于对接各种支付渠道。

支付插件结构

content/plugins/
└── my_pay/ # 支付插件目录
    ├── my_pay.php # 主文件
    ├── my_pay_setting.php # 配置页面
    ├── icon-btn.png # 支付按钮图标(建议 100x40)
    └── qrcode.php # 扫码支付页面(可选)

支付插件核心函数

支付插件需要实现以下核心函数:

函数说明必须
init_插件名()初始化支付方式,注册到支付列表
pay_插件名($order, $list)发起支付请求
插件名CheckSign($type)验证支付回调签名

完整示例

<?php
/*
Plugin Name: 示例支付
Version: 1.0.0
Plugin URL:
Description: 示例支付接口插件
Author: DCSHOP
Author URL:
Ui: Layui
*/

defined('DC_ROOT') || exit('access denied!');

/**
 * 初始化支付方式
 * 钩子:mode_payment
 */
function init_my_pay() {
    $storage = Storage::getInstance('my_pay');
    $enabled = $storage->getValue('enabled');
    
    if ($enabled !== 'y') {
        return;
    }
    
    // 注册支付方式到全局数组
    $GLOBALS['mode_payment'][] = [
        'plugin_name' => 'my_pay',           // 插件名,与目录名一致
        'icon' => './content/plugins/my_pay/icon-btn.png',  // 支付按钮图标
        'title' => '示例支付',                // 显示名称
        'unique' => 'my_pay',                // 唯一标识,全局不可重复
        'name' => '示例支付'                  // 支付方式名称
    ];
}

// 注册到支付方式钩子
addAction('mode_payment', 'init_my_pay');

/**
 * 发起支付
 * 函数命名规则:pay_插件名
 * 
 * @param array $order_info 主订单信息
 * @param array $order_list 子订单列表
 */
function pay_my_pay($order_info, $order_list) {
    // 获取插件配置
    $storage = Storage::getInstance('my_pay');
    $merchant_id = $storage->getValue('merchant_id');
    $secret_key = $storage->getValue('secret_key');
    
    // 检查订单是否过期
    if ($order_info['expire_time'] <= time()) {
        emMsg('订单已过期,请重新发起支付', 'javascript:window.close();');
    }
    
    // 构建支付参数
    $params = [
        'merchant_id' => $merchant_id,
        'out_trade_no' => $order_info['out_trade_no'],
        'amount' => round($order_info['amount'] / 100, 2),  // 金额单位:元
        'notify_url' => DC_URL . 'action/notify/my_pay',    // 异步回调地址
        'return_url' => DC_URL . 'action/return/my_pay',    // 同步跳转地址
        'timestamp' => time(),
    ];
    
    // 生成签名
    ksort($params);
    $sign_str = http_build_query($params) . '&key=' . $secret_key;
    $params['sign'] = md5($sign_str);
    
    // 方式1:跳转到支付网关
    $gateway_url = 'https://pay.example.com/gateway';
    header('Location: ' . $gateway_url . '?' . http_build_query($params));
    exit;
    
    // 方式2:生成二维码(扫码支付)
    // $qr_url = getQrCodeUrl($params);
    // header('Location: ?plugin=my_pay&out_trade_no=' . $order_info['out_trade_no'] . '&qr=' . urlencode($qr_url));
    // exit;
}

/**
 * 验证支付回调签名
 * 函数命名规则:插件名CheckSign
 * 
 * @param string $notify_type 回调类型:'return'(同步) 或 'notify'(异步)
 * @return array|false 成功返回订单信息数组,失败返回 false
 */
function my_payCheckSign($notify_type) {
    $storage = Storage::getInstance('my_pay');
    $secret_key = $storage->getValue('secret_key');
    
    // 获取回调参数
    if ($notify_type == 'return') {
        $params = $_GET;
    } else {
        $params = $_POST;
        // 或者从 php://input 获取
        // $params = json_decode(file_get_contents('php://input'), true);
    }
    
    // 验证签名
    $sign = $params['sign'] ?? '';
    unset($params['sign']);
    ksort($params);
    $sign_str = http_build_query($params) . '&key=' . $secret_key;
    $expected_sign = md5($sign_str);
    
    if ($sign !== $expected_sign) {
        return false;
    }
    
    // 验证支付状态
    if (($params['status'] ?? '') !== 'success') {
        return false;
    }
    
    // 返回订单信息
    return [
        'timestamp' => time(),
        'out_trade_no' => $params['out_trade_no'],  // 商户订单号
        'up_no' => $params['trade_no'] ?? '',       // 上游订单号
    ];
}

回调地址说明

类型地址格式说明
异步回调 DC_URL/action/notify/插件名 支付成功后,支付平台服务器调用此地址通知支付结果
同步跳转 DC_URL/action/return/插件名 用户支付完成后,浏览器跳转到此地址
注意:异步回调成功后需要输出 success 或支付平台要求的成功响应,否则支付平台会重复发送回调。

订单信息字段

主订单 $order_info

字段类型说明
idint订单ID
out_trade_nostring商户订单号
amountint订单金额(单位:分)
statusint订单状态:0待支付 1已支付待发货 2已完成
expire_timeint订单过期时间戳
create_timeint订单创建时间戳
user_idint用户ID(游客为0)

扫码支付页面

如果是扫码支付,需要创建一个二维码展示页面:

<?php
// 在插件目录下创建 qrcode.php 或在主文件中处理

$out_trade_no = Input::getStrVar('out_trade_no');
$qr_code = Input::getStrVar('qr');

// 引入二维码生成库或使用在线API
$qr_img = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' . urlencode($qr_code);
?>
<!DOCTYPE html>
<html>
<head>
    <title>扫码支付</title>
</head>
<body>
    <div style="text-align: center; padding: 50px;">
        <h2>请使用手机扫码支付</h2>
        <img src="<?= $qr_img ?>" alt="支付二维码">
        <p>订单号:<?= htmlspecialchars($out_trade_no) ?></p>
    </div>
    
    <script>
    // 轮询检查支付状态
    setInterval(function() {
        fetch('/api.php?action=check_order&out_trade_no=<?= $out_trade_no ?>')
            .then(r => r.json())
            .then(data => {
                if (data.status === 'paid') {
                    location.href = '/order_result.php?out_trade_no=<?= $out_trade_no ?>';
                }
            });
    }, 3000);
    </script>
</body>
</html>