Typecho 1.3 打赏功能开发教程:从零到一实现博客变现
引言
在内容创作日益普及的今天,打赏功能已成为许多博主与读者互动、获得经济支持的重要方式。Typecho作为一款轻量级、高性能的开源博客系统,虽然功能简洁,但通过插件扩展可以轻松实现打赏功能。本文将深入探讨如何在Typecho 1.3版本中开发一个完整的打赏功能模块,涵盖从需求分析、代码实现到界面优化的全过程。
打赏功能的实现不仅能够为博主带来额外收入,还能增强读者与博主之间的互动关系。相比其他博客系统,Typecho的插件开发相对简单,但需要一定的PHP和前端知识。本教程将带领你一步步构建一个功能完善、界面美观的打赏插件。
打赏功能需求分析与设计
功能需求分析
在开始编码之前,我们需要明确打赏功能的核心需求:
- 支付方式支持:至少支持微信支付和支付宝两种主流支付方式
- 金额设置:允许用户自定义打赏金额或选择预设金额
- 打赏记录:后台可查看所有打赏记录,包括时间、金额、支付方式等信息
- 界面展示:在前端文章页面合适位置展示打赏按钮和二维码
- 通知功能:打赏成功后向博主发送通知(邮件或站内信)
- 统计功能:统计总打赏金额、次数等数据
技术架构设计
基于Typecho 1.3的插件架构,我们设计以下技术方案:
- 插件结构:遵循Typecho插件标准目录结构
- 数据库设计:创建独立的数据表存储打赏记录
- 支付接口:集成第三方支付SDK(如PayJS、Ping++等)
- 前端实现:使用HTML/CSS/JavaScript创建响应式打赏界面
- 安全性考虑:实现支付回调验证、防刷单机制
开发环境准备
环境要求
在开始开发前,请确保你的环境满足以下要求:
- Typecho 1.3 或更高版本
- PHP 7.0+(建议7.4以上)
- MySQL 5.6+ 或 MariaDB 10.0+
- 支持URL重写(mod_rewrite)
- 一个可用的第三方支付账户(用于测试)
插件目录结构
创建插件目录结构如下:
Reward/
├── Plugin.php # 插件主文件
├── Reward/
│ ├── Db/ # 数据库操作类
│ ├── Helper/ # 辅助函数
│ ├── Model/ # 数据模型
│ └── View/ # 视图文件
├── assets/ # 静态资源
│ ├── css/
│ ├── js/
│ └── images/
├── install.sql # 安装SQL
└── uninstall.sql # 卸载SQL数据库设计与实现
创建数据表
我们需要创建一个数据表来存储打赏记录。在install.sql文件中添加以下SQL:
CREATE TABLE `typecho_reward_records` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`post_id` int(10) unsigned NOT NULL COMMENT '文章ID',
`amount` decimal(10,2) NOT NULL COMMENT '打赏金额',
`payment_method` varchar(20) NOT NULL COMMENT '支付方式',
`transaction_id` varchar(100) DEFAULT NULL COMMENT '交易ID',
`payer` varchar(100) DEFAULT NULL COMMENT '支付者信息',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '支付状态',
`created_at` int(10) unsigned NOT NULL COMMENT '创建时间',
`updated_at` int(10) unsigned DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_post_id` (`post_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打赏记录表';数据模型类
创建Reward/Model/Record.php文件,定义数据模型:
<?php
class Reward_Model_Record extends Typecho_Db
{
// 表名
const TABLE_NAME = 'typecho_reward_records';
// 支付状态常量
const STATUS_PENDING = 0; // 待支付
const STATUS_SUCCESS = 1; // 支付成功
const STATUS_FAILED = 2; // 支付失败
/**
* 添加打赏记录
*/
public static function addRecord($data)
{
$db = Typecho_Db::get();
$data['created_at'] = time();
return $db->query($db->insert(self::TABLE_NAME)->rows($data));
}
/**
* 更新打赏记录状态
*/
public static function updateStatus($transactionId, $status, $payer = null)
{
$db = Typecho_Db::get();
$updateData = [
'status' => $status,
'updated_at' => time()
];
if ($payer) {
$updateData['payer'] = $payer;
}
return $db->query($db->update(self::TABLE_NAME)
->rows($updateData)
->where('transaction_id = ?', $transactionId));
}
/**
* 获取文章打赏统计
*/
public static function getPostStats($postId)
{
$db = Typecho_Db::get();
return $db->fetchRow($db->select('COUNT(*) as count', 'SUM(amount) as total')
->from(self::TABLE_NAME)
->where('post_id = ?', $postId)
->where('status = ?', self::STATUS_SUCCESS));
}
}
?>插件主文件开发
插件基本信息
在Plugin.php文件中定义插件基本信息:
<?php
/**
* Typecho打赏插件
*
* @package Reward
* @author YourName
* @version 1.0.0
* @link https://yourwebsite.com
*/
class Reward_Plugin implements Typecho_Plugin_Interface
{
/**
* 激活插件
*/
public static function activate()
{
// 创建数据库表
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
try {
$sql = file_get_contents(dirname(__FILE__) . '/install.sql');
$sql = str_replace('typecho_', $prefix, $sql);
$db->query($sql);
} catch (Typecho_Db_Exception $e) {
throw new Typecho_Plugin_Exception('安装失败:' . $e->getMessage());
}
// 注册文章输出钩子
Typecho_Plugin::factory('Widget_Archive')->footer = array('Reward_Plugin', 'renderRewardBox');
// 注册后台管理页面
Typecho_Plugin::factory('admin/menu.php')->navBar = array('Reward_Plugin', 'renderAdminMenu');
return '插件激活成功,请配置支付参数';
}
/**
* 禁用插件
*/
public static function deactivate()
{
// 可选:禁用时删除数据表
// $db = Typecho_Db::get();
// $db->query("DROP TABLE IF EXISTS `{$db->getPrefix()}reward_records`");
return '插件已禁用';
}
/**
* 插件配置面板
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
// 支付配置
$payTitle = new Typecho_Widget_Helper_Form_Element_Text('wechat_appid',
null, null, _t('微信AppID'), _t('微信支付的AppID'));
$form->addInput($payTitle);
$alipayAppid = new Typecho_Widget_Helper_Form_Element_Text('alipay_appid',
null, null, _t('支付宝AppID'), _t('支付宝的AppID'));
$form->addInput($alipayAppid);
// 金额设置
$amounts = new Typecho_Widget_Helper_Form_Element_Text('reward_amounts',
null, '2,5,10,20,50', _t('打赏金额选项'), _t('用逗号分隔的金额列表,单位:元'));
$form->addInput($amounts);
// 自定义金额
$customAmount = new Typecho_Widget_Helper_Form_Element_Radio('allow_custom_amount',
array('1' => _t('允许'), '0' => _t('禁止')), '1', _t('允许自定义金额'));
$form->addInput($customAmount);
// 显示位置
$position = new Typecho_Widget_Helper_Form_Element_Select('display_position',
array(
'after_content' => _t('文章内容之后'),
'before_content' => _t('文章内容之前'),
'sidebar' => _t('侧边栏'),
'floating' => _t('浮动按钮')
), 'after_content', _t('打赏框显示位置'));
$form->addInput($position);
}
/**
* 个人用户配置面板
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form) {}
/**
* 在文章底部渲染打赏框
*/
public static function renderRewardBox()
{
// 只在前台文章页面显示
if (!Typecho_Widget::widget('Widget_Archive')->is('single')) {
return;
}
// 获取配置
$options = Helper::options();
$pluginOptions = $options->plugin('Reward');
// 加载CSS和JS
$cssUrl = Helper::options()->pluginUrl . '/Reward/assets/css/reward.css';
$jsUrl = Helper::options()->pluginUrl . '/Reward/assets/js/reward.js';
echo '<link rel="stylesheet" href="' . $cssUrl . '">';
// 渲染打赏HTML
include dirname(__FILE__) . '/Reward/View/reward-box.php';
echo '<script src="' . $jsUrl . '"></script>';
}
}
?>前端界面实现
CSS样式设计
创建assets/css/reward.css文件:
/* 打赏容器样式 */
.reward-container {
margin: 40px 0;
padding: 20px;
border-radius: 8px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
text-align: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.reward-title {
font-size: 1.5em;
color: #333;
margin-bottom: 15px;
font-weight: bold;
}
.reward-desc {
color: #666;
margin-bottom: 20px;
line-height: 1.6;
}
/* 金额选择按钮 */
.amount-buttons {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.amount-btn {
padding: 10px 20px;
border: 2px solid #4CAF50;
border-radius: 25px;
background: white;
color: #4CAF50;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
}
.amount-btn:hover,
.amount-btn.active {
background: #4CAF50;
color: white;
}
/* 自定义金额输入 */
.custom-amount {
margin: 15px 0;
}
.custom-amount input {
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
width: 150px;
text-align: center;
font-size: 16px;
}
/* 支付方式选择 */
.payment-methods {
display: flex;
justify-content: center;
gap: 20px;
margin: 20px 0;
}
.payment-method {
padding: 10px 20px;
border: 2px solid #ddd;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.payment-method.active {
border-color: #2196F3;
background: #e3f2fd;
}
.payment-icon {
width: 40px;
height: 40px;
margin-bottom: 5px;
}
/* 打赏按钮 */
.reward-btn {
padding: 12px 40px;
background: linear-gradient(135deg, #FF9800 0%, #FF5722 100%);
color: white;
border: none;
border-radius: 25px;
font-size: 18px;
cursor: pointer;
transition: transform 0.3s ease;
}
.reward-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(255, 87, 34, 0.3);
}
/* 二维码弹窗 */
.qrcode-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 9999;
align-items: center;
justify-content: center;
}
.qrcode-content {
background: white;
padding: 30px;
border-radius: 10px;
text-align: center;
max-width: 400px;
width: 90%;
}
.qrcode-img {
width: 200px;
height: 200px;
margin: 20px auto;
}
.close-modal {
position: absolute;
top: 10px;
right: 10px;
font-size: 24px;
cursor: pointer;
color: #666;
}JavaScript交互逻辑
创建assets/js/reward.js文件:
(function() {
// 初始化打赏功能
document.addEventListener('DOMContentLoaded', function() {
initRewardSystem();
});
function initRewardSystem() {
// 金额按钮点击事件
document.querySelectorAll('.amount-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.amount-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
document.getElementById('reward-amount').value = this.dataset.amount;
});
});
// 支付方式选择
document.querySelectorAll('.payment-method').forEach(method => {
method.addEventListener('click', function() {
document.querySelectorAll('.payment-method').forEach(m => m.classList.remove('active'));
this.classList.add('active');
});
});
// 打赏按钮点击
const rewardBtn = document.getElementById('reward-action');
if (rewardBtn) {
rewardBtn.addEventListener('click', function() {
const amount = document.getElementById('reward-amount').value;
const paymentMethod = document.querySelector('.payment-method.active');
if (!amount || amount <= 0) {
alert('请选择或输入打赏金额');
return;
}
if (!paymentMethod) {
alert('请选择支付方式');
return;
}
// 显示二维码弹窗
showQRCode(amount, paymentMethod.dataset.method);
});
}
// 关闭弹窗
const closeBtn = document.querySelector('.close-modal');
if (closeBtn) {
closeBtn.addEventListener('click', hideQRCode);
}
// 点击弹窗背景关闭
const modal = document.getElementById('qrcode-modal');
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === this) {
hideQRCode();
}
});
}
}
function showQRCode(amount, method) {
const modal = document.getElementById('qrcode-modal');
const qrcodeImg = document.getElementById('qrcode-image');
// 这里应该通过AJAX请求获取二维码
// 为了简化示例,我们使用静态图片
qrcodeImg.src = method === 'wechat'
? 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wechatpay://' + amount
: 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=alipay://' + amount;
document.getElementById('qrcode-amount').textContent = amount + '元';
document.getElementById('qrcode-method').textContent = method === 'wechat' ? '微信支付' : '支付宝';
modal.style.display = 'flex';
// 模拟支付成功(实际开发中应该使用WebSocket或轮询检查支付状态)
setTimeout(checkPaymentStatus, 3000);
}
function hideQRCode() {
document.getElementById('qrcode-modal').style.display = 'none';
}
function checkPaymentStatus() {
// 这里应该通过AJAX检查支付状态
// 如果支付成功,显示成功提示并关闭弹窗
const success = Math.random() > 0.5; // 模拟随机结果
if (success) {
alert('支付成功!感谢您的支持!');
hideQRCode();
// 更新打赏统计
updateRewardStats();
}
}
function updateRewardStats() {
// 更新页面上的打赏统计信息
const statsElement = document.getElementById('reward-stats');
if (statsElement) {
// 这里应该通过AJAX获取最新统计
// 暂时使用模拟数据
const currentCount = parseInt(statsElement.dataset.count) || 0;
const currentTotal = parseFloat(statsElement.dataset.total) || 0;
const amount = parseFloat(document.getElementById('reward-amount').value);
statsElement.dataset.count = currentCount + 1;
statsElement.dataset.total = currentTotal + amount;
statsElement.innerHTML = `已有 ${currentCount + 1} 人打赏,累计 ${(currentTotal + amount).toFixed(2)} 元`;
}
}
})();支付接口集成
微信支付集成示例
创建Reward/Helper/WechatPay.php文件:
<?php
class Reward_Helper_WechatPay
{
private $appId;
private $mchId;
private $apiKey;
public function __construct($appId, $mchId, $apiKey)
{
$this->appId = $appId;
$this->mchId = $mchId;
$this->apiKey = $apiKey;
}
/**
* 生成支付二维码
*/
public function createQRCode($amount, $description, $orderNo)
{
// 这里应该调用微信支付API
// 由于微信支付需要商户资质,这里仅展示流程
$params = [
'appid' => $this->appId,
'mch_id' => $this->mchId,
'nonce_str' => $this->generateNonceStr(),
'body' => $description,
'out_trade_no' => $orderNo,
'total_fee' => $amount * 100, // 微信支付单位为分
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
'notify_url' => $this->getNotifyUrl(),
'trade_type' => 'NATIVE',
'product_id' => $orderNo
];
$params['sign'] = $this->generateSign($params);
// 调用统一下单API
$result = $this->postXmlCurl($this->arrayToXml($params),
'https://api.mch.weixin.qq.com/pay/unifiedorder');
$resultArray = $this->xmlToArray($result);
if ($resultArray['return_code'] == 'SUCCESS' &&
$resultArray['result_code'] == 'SUCCESS') {
return $resultArray['code_url']; // 二维码链接
}
return false;
}
/**
* 验证支付回调
*/
public function verifyNotify($xml)
{
$data = $this->xmlToArray($xml);
if (!isset($data['sign'])) {
return false;
}
$sign = $data['sign'];
unset($data['sign']);
return $this->generateSign($data) == $sign;
}
// 其他辅助方法...
}
?>后台管理界面
打赏记录管理
创建后台管理页面,显示打赏记录:
<?php
// 在Plugin.php中添加管理页面
public static function renderAdminMenu($nav)
{
$nav['reward'] = array(
'text' => '打赏管理',
'url' => Typecho_Common::url('/extending.php?panel=Reward%2Fadmin%2Frecords.php',
Helper::options()->adminUrl),
'icon' => 'heart'
);
return $nav;
}
?>创建Reward/admin/records.php文件:
<?php
if (!isset($_GET['panel']) || !Typecho_Widget::widget('Widget_User')->hasLogin()) {
exit;
}
require_once 'header.php';
// 获取打赏记录
$db = Typecho_Db::get();
$records = $db->fetchAll($db->select()
->from('table.reward_records')
->order('created_at', Typecho_Db::SORT_DESC)
->page(1, 20));
// 统计信息
$stats = $db->fetchRow($db->select('COUNT(*) as count', 'SUM(amount) as total')
->from('table.reward_records')
->where('status = ?', 1));
?>
<div class="typecho-page-title">
<h2>打赏记录管理</h2>
</div>
<div class="typecho-page-main">
<div class="typecho-table-wrap">
<table class="typecho-table">
<thead>
<tr>
<th>ID</th>
<th>文章标题</th>
<th>金额</th>
<th>支付方式</th>
<th>支付者</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<?php foreach ($records as $record): ?>
<tr>
<td><?php echo $record['id']; ?></td>
<td>
<?php
$post = $db->fetchRow($db->select('title')
->from('table.contents')
->where('cid = ?', $record['post_id']));
echo $post ? $post['title'] : '文章已删除';
?>
</td>
<td><?php echo $record['amount']; ?>元</td>
<td><?php echo $record['payment_method']; ?></td>
<td><?php echo $record['payer'] ?: '匿名'; ?></td>
<td>
<?php
$statusText = [
0 => '<span style="color:orange">待支付</span>',
1 => '<span style="color:green">成功</span>',
2 => '<span style="color:red">失败</span>'
];
echo $statusText[$record['status']];
?>
</td>
<td><?php echo date('Y-m-d H:i:s', $record['created_at']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="typecho-table-info">
<p>总计:<?php echo $stats['count']; ?> 笔打赏,共 <?php echo $stats['total'] ?: '0'; ?> 元</p>
</div>
</div>
<?php require_once 'footer.php'; ?>插件优化与安全考虑
性能优化建议
- 缓存机制:对打赏统计数据进行缓存,减少数据库查询
- 异步处理:支付回调使用队列异步处理,提高响应速度
- CDN加速:静态资源使用CDN加速
- 数据库索引:为常用查询字段添加索引
安全措施
- 输入验证:对所有用户输入进行严格验证和过滤
- SQL防注入:使用Typecho的查询构建器,避免SQL注入
- XSS防护:输出时使用htmlspecialchars转义
- CSRF防护:重要操作添加CSRF令牌验证
- 支付安全:验证支付回调签名,防止伪造请求
- 频率限制:限制同一IP的打赏频率,防止刷单
兼容性考虑
- 响应式设计:确保打赏界面在各种设备上正常显示
- 浏览器兼容:支持主流浏览器(Chrome、Firefox、Safari、Edge)
- Typecho版本:确保插件兼容Typecho 1.3及以上版本
- PHP版本:支持PHP 7.0及以上版本
测试与部署
测试要点
- 功能测试:测试所有打赏流程,包括支付成功、失败、取消等场景
- 兼容性测试:在不同浏览器和设备上测试界面显示
- 性能测试:测试高并发下的支付处理能力
- 安全测试:进行常见的安全漏洞测试
部署步骤
- 将插件文件夹上传到Typecho的
usr/plugins/目录 - 在Typecho后台激活插件
- 配置支付参数(AppID、密钥等)
- 配置打赏金额选项和显示位置
- 测试打赏功能是否正常工作
总结
通过本教程,我们完整地开发了一个Typecho 1.3的打赏功能插件。从需求分析、数据库设计、代码实现到界面优化,我们涵盖了插件开发的各个环节。这个插件具有以下特点:
- 功能完善:支持微信支付和支付宝,包含完整的打赏流程
- 用户体验良好:美观的界面设计,流畅的交互体验
- 易于管理:后台可查看打赏记录和统计信息
- **
全部回复 (0)
暂无评论
登录后查看 0 条评论,与更多用户互动