充值系列——充值系統業務邏輯層實現(三)
阿新 • • 發佈:2018-11-10
上一篇文章主要說明充值的執行邏輯和控制層的設計,這篇文章主要討論充值業務層的具體實現。
正如上一篇文章所說到的,生成訂單需要如下幾個步驟:
(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為例,進一步說明支付系統,同時關注支付安全的問題。