充值系列——充值系统业务逻辑层实现(三)

上一篇文章主要说明充值的执行逻辑和控制层的设计,这篇文章主要讨论充值业务层的具体实现。

正如上一篇文章所说到的,生成订单需要如下几个步骤:

(1)实例化操作人 (操作人)

(2)实例化产品模型 (获取产品的详细信息)

(3)实例化订单模型 (生成一笔状态为“交易中”的订单)

(4)实例化支付平台模型 (把生成的订单返回给客户端,并引导用于去充值平台)

一个完整的充值系统需要完善以上4个基本的模型类。详细的UML图如下:

支付平台依赖操作人类,订单类,产品类,助手类。

支付平台工厂类

class Model_Payment
{
    // 对应关系为:'渠道ID' => '渠道类名'
    private static $_channels = array(
        '10' => 'alipay',
        '20' => 'applestore'
    );
 
    // 缺省的充值渠道
    const DEFAULT_CHANNEL_ID = 40;
 
    public function factory($channelId) 
    {
        if isset(self::$_channels[$channelId]) : throws('渠道不合法');
 
        $className = 'Model_Payment_Channel_' . ucfirst(self::$_chanels[$channelId]);
 
        return new $className;
    }
}

支付平台类

如上一篇文章所谈到的,支付平台类必须提供三个方法:生成订单,异步响应,同步响应。 对应不同的支付平台,异步响应,同步响应的处理方式不同,下一篇博客将详细讨论,这里主要讲解生成订单函数。

function createOrder(Model_User $user, Model_Payment_Product $product, array $extraData = array())
    {
        if (! $product instanceof Model_Payment_ProductNull) {
            if ($product['channel_id'] != $this->_channelId) {
                throw new Model_Payment_Exception_Common(_('购买的产品和当前充值渠道不匹配'));
            }
        }
 
        // 执行新增订单
        $orderResult = $this->_insertOrder($user, $product, $extraData);
 
        // 组装生成订单后的返回结果
        return $this->_buildCreateReturn($orderResult, $product);
    }

执行新增订单的函数的时候,必须返回新增函数的订单号。

protected function _insertOrder(Model_User $user, Model_Payment_Product $product, array $extraData = array())
    {
 
        $setArr = array(
            'status'        => Model_Payment_Order::STATUS_UNPAID, // 未支付
            'create_time'   => $GLOBALS['_DATE'],
            'uid'           => $user['uid'],
            'product_name'  => $product['name'],
             // 此处省略其他订单信息
            'channel_id'    => $this->_channelId,       // 充值渠道
        );
 
        if (! $orderSn = Model_Payment_Order::create($setArr)) {
            throw new Model_Payment_Exception_Common(_('保存订单失败'));
        }
 
        return array(
            'order_sn'    => $orderSn,  // 必须返回订单详细
    }

我方组装生成订单后的返回结果
情况1:以JSON格式输出响应给客户端(缺省)
情况2:以HTML表单格式自动提交GET/POST请求
情况3:以URL信号的方式重新跳转

 protected function _buildCreateReturn(array $orderResult, Model_Payment_Product $product)
    {
        return json_encode(array(
            'status'         => 'success',
            'productId'      => $product['id'],                  // 我方产品Id
            'thirdProductId' => $product['third_product_id'],    // 对应第三方平台的产品Id
            'totalPrice'     => $orderResult['total_price'],     // 订单总价
            'orderSn'        => $orderResult['order_sn'],        // 内部订单流水号
            'subject'        => $product['name'],                // 订单名称
            'description'    => _('购买') . $product['name'],    // 订单备注
        ));
    }

订单类

主要需要实现的方法有:插入订单,更改订单信息,获取订单详细,验证异步响应回来的订单信息中,包含的产品价格是否和当前订单中的一致,这一步特别主要。

public function create($setArr)
    {
        // 生成新的订单流水号 (这个方法在这个系列(一)中有说明)
        $orderSn = self::createSn();
        $setArr['order_sn'] = $orderSn;
 
        // 下单时间
        if (! isset($setArr['create_time'])) {
            $setArr['create_time'] = $GLOBALS['_DATE'];
        }
 
        if (! Dao('Order_Orders')->insert($setArr)) {
            return false;
        }
 
        return $orderSn;
    }

获取订单信息

function __construct($orderSn)
    {
        if (! $orderSn) {
            throw new Model_Payment_Exception_Common('Invalid OrderSn');
        }
 
        if (! $this->_prop = Dao('Order_Orders')->get($orderSn)) {
            throw new Model_Payment_Exception_Common(_('订单信息不存在。') . 'OrderSn:' . $orderSn);
        }
    }

还有没有实现的方法请脑补。

产品类

主要需要实现的方法有:根据渠道获取产品列表,获取产品的信息,判断产品是否处于促销中等

function __construct($productId = 0)
    {
        if ($productId < 1) {
            throw new Model_Payment_Exception_Common('Invalid ProductId');
        }
 
        if (! $this->_prop = Dao('Order_Product')->get($productId)) {
            throw new Model_Payment_Exception_Common(_('购买的产品不存在') . 'ProductId:' . $productId);
        }
 
        // 拼接产品名称(因为多语言版本)
        $this->_prop['name'] = _('手机大航海') . $this->_prop['gold_value'] . _('金块');
    }

获取产品列表

function getListByChannel($channelId)
    {
        return Dao('Order_Product')->getListByChannel($channelId);
    }

助手类
主要实现的方法有:加载配置文件,组装跳转的URL,以表单HTML形式构造请求

function buildRequestForm($action, array $params, $method, $btnName = null)
    {
        $html = "<form id='payformsubmit' action='" . $action . "' method='" . $method . "'>";
 
        if ($params) {
            foreach ($params as $key => $value) {
                $html .= "<input type='hidden' name='" . $key . "' value='" . $value ."' />";
            }
        }
 
        $btnName = $btnName ?: _('订单确认');
 
        $html .= "<input type='submit' value='" . $btnName . "' />";
        $html .= "</form>";
        $html .= "<script>document.getElementById('payformsubmit').submit();</script>";
 
        return $html;
    }

下一篇文章将以alipay为例,进一步说明支付系统,同时关注支付安全的问题。

猜你喜欢

转载自blog.csdn.net/uisoul/article/details/83036743