使用者錢包(賬戶)設計

Euan發表於2020-03-21

前言

 在涉及關於金錢的業務系統時,一定要注意資料安全。通常可以把財務相關的資料庫,獨立於其他的業務系統資料庫。從表的設計角度來看。應該紀錄每一步資金的流向,方便資料追蹤下面來開始設計資料表。

1,表設計

    這裡主要用到的表有。使用者錢包表,流水紀錄表,充值訂單表,支付紀錄表,提現表。
DROP TABLE IF EXISTS `user_wallet`;
CREATE TABLE `user_wallet` (
  `user_uuid` char(32)  NOT NULL COMMENT '使用者user_uuid',
  `wallet_income` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '錢包總收入額',
  `wallet_outcome` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '錢包總支出額',
  `balance_fee` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '錢包總可用餘額',
  `check_sign`  varchar(100) DEFAULT '' COMMENT '用於安全檢查,檢查不通過為異常。',
  `update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`user_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用者錢包';
    上面的錢包表中,check_sign欄位,是有效保證資料安全,不被輕易篡改的方法。sign欄位的值是當前一條紀錄的資料加密計算值。在每次對這張表進行操作時。都要進行check_sign欄位簽名驗證。通過則繼續。不通過,則可以根據具體情況採取措施。比如傳送通知到相關人員。

DROP TABLE IF EXISTS `user_wallet_log`;
CREATE TABLE `user_wallet_log` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto id',
  `user_uuid` char(32) DEFAULT '' COMMENT '用uuid',
  `number` varchar(32) NOT NULL DEFAULT '' COMMENT '流水號',
  `target_type` smallint(5) unsigned DEFAULT '0' COMMENT '業務型別,1:充值,2:提現  3:下單',
  `target_uuid` char(32) DEFAULT '' COMMENT '來源uuid(如提現uuid)',
  `action_type` smallint(5) unsigned DEFAULT '0' COMMENT '操作型別,1:充值,2:提現,3:訂單',
  `fee` decimal(10,2) DEFAULT '0.00' COMMENT '變動的金額,正負數。',
  `original_account_json`  varchar(1000) DEFAULT '賬戶變更前的資料 json儲存',
 `dispose_account_json`  varchar(1000) DEFAULT '賬戶變更後的資料 json儲存',
  `status` smallint(5) unsigned DEFAULT '0' COMMENT '處理狀態1,處理完成,0未完成',
  `resultType`  smallint(5) unsigned DEFAULT '0' COMMENT '處理結果,0:沒有變更,1:有變更。',
  `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用者錢包流水記錄表';
    流水紀錄表中的original_account_json和dispose_account_json欄位,可以用來排查賬戶異常,方便梳理對帳。
DROP TABLE IF EXISTS `user_top_up_order`;
CREATE TABLE `user_top_up_order` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto id',
  `user_uuid` char(32) NOT NULL DEFAULT '' COMMENT '使用者ID',
  `order_num` char(32) NOT NULL COMMENT '訂單號',
  `amounts` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '訂單總金額',
  `currency` varchar(8) NOT NULL DEFAULT 'CNY' COMMENT '貨幣型別',
  `pay_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '實際支付金額',
  `exchange` decimal(10,4) NOT NULL DEFAULT '0.00' COMMENT '匯率',
  `status` tinyint(5) NOT NULL DEFAULT '0' COMMENT '支付狀態:0待支付,1已支付,100已取消',
  `update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  `deleted` tinyint(5) NOT NULL DEFAULT '0' COMMENT '0未刪除1已刪除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='充值訂單';
    充值訂單沒什麼好說的
DROP TABLE IF EXISTS `user_withdraw_cash_list`;
CREATE TABLE `user_withdraw_cash_list` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto id',
  `user_uuid` varchar(32) NOT NULL COMMENT '申請使用者uuid',
  `withdraw_way` tinyint(1) NOT NULL DEFAULT '1' COMMENT '提現(渠道)方式 1銀行轉賬',
  `withdraw_status` smallint(5) unsigned DEFAULT '0' COMMENT '處理狀態。 1發起申請(待稽核理)前臺顯示處理中,2提現成功,3稽核不通過',
  `number` char(32) DEFAULT '' COMMENT '提現單號',
  `receivable_account` varchar(32) DEFAULT '' COMMENT '收款賬戶',
  `name` varchar(30) DEFAULT '' COMMENT '收款人姓名',
  `address` varchar(100) DEFAULT '' COMMENT '開戶行地址',
  `withdraw_fee` decimal(10,2) DEFAULT '0.00' COMMENT '提現金額',
  `content` varchar(500) DEFAULT '' COMMENT '稽核不通過原因',
  `verify_user` varchar(32) DEFAULT '' COMMENT '稽核人',
  `action_user` varchar(32) DEFAULT '' COMMENT '操作人',
  `action_at` datetime DEFAULT NULL COMMENT '稽核時間',
  `sent_notice_at` datetime DEFAULT NULL COMMENT '傳送通知時間',
  `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  `update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否刪除:0未刪除,1已刪除',
  PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='提現記錄表';
    一般來說,有充值,就有提現。很多業務系統中沒有充值,也會有提現場景。最後一張就是支付紀錄表了。
DROP TABLE IF EXISTS `payment_list`;
CREATE TABLE `payment_list` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'auto id',
  `user_uuid` varchar(32) NOT NULL DEFAULT '' COMMENT '使用者uuid',
  `pay_num` bigint(20) unsigned DEFAULT '0' COMMENT '支付號',
  `trade_no` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '第三方付款成功交易號',
  `order_type` smallint(5) unsigned DEFAULT '0' COMMENT '訂單型別 1充值 ',
  `order_uuid` char(32) DEFAULT '' COMMENT '充值訂單表uuid',
  `pay_way` smallint(5) unsigned DEFAULT '0' COMMENT '付款方式 1微信 2支付寶 ',
  `pay_status` smallint(5) unsigned DEFAULT '0' COMMENT '支付狀態 1 支付成功,2 支付失敗 ',
  `step` smallint(5) unsigned DEFAULT '0' COMMENT '步驟 1 建立支付,2 支付回撥通知',
  `post_status` smallint(5) unsigned DEFAULT '0' COMMENT '請求支付狀態,1:成功,2:失敗',
  `return_status` smallint(5) unsigned DEFAULT '0' COMMENT '回撥狀態,1:成功,2:失敗',
  `total_fee` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '支付價格',
  `result_code`  varchar(30) DEFAULT '' COMMENT '第三方返回錯誤碼',
  `err_code_str`  varchar(255) DEFAULT '' COMMENT '第三方返回的錯誤記錄',
  `currency` varchar(8) NOT NULL DEFAULT 'HKD' COMMENT '貨幣型別',
  `ip2long` int(10) unsigned DEFAULT '0' COMMENT 'ip2long',
  `post_json` varchar(2000) DEFAULT '' COMMENT '提交post json 資料',
  `return_json` varchar(2000) DEFAULT '' COMMENT '回撥post return data json',
  `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  `update_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  `success_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '支付成功時間',
`procedure_kb mediumint(8) unsigned DEFAULT '0' COMMENT '手續費比例,千位比。6/1000',

`procedure_fee`  decimal(10,2) unsigned DEFAULT '0.00' COMMENT '支付渠道收取手續費金額',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付記錄列表';
以上,使用者錢包功能所需表就設計好了。之前一篇提到的支付紀錄表也面世了。因為很多公司並沒有這張表,或者說只有一張簡單的表紀錄第三方交易單號的資訊和訂單資訊。而且後臺管理系統沒有頁面展示。導致客服運營等人員,經常反饋支付失敗的問題。很多時候只是網路延時導致訂單回撥不及時,而沒有修改訂單狀態。而我們開發則不得不停下手中的活,去排查相關的問題。

2 簡要流程

這裡就不畫流程圖和時序圖了。簡單說下步驟
1.何時建立使用者錢包紀錄?。新使用者在註冊時可建立。老使用者寫指令碼建立。
2.建立使用者錢包紀錄根據用的錢包紀錄資訊生產一個sign。並同時建立一條流水紀錄。
3.操作錢包表時,要先獲取一條紀錄。生產一個sign值,然後根據紀錄的sign值進行對比。相同則繼續走正常的步驟。不同時,則應該終止後續操作,併發通知給相關人員進行排查異常。
4.重新生成sign值,並正常更新資料,紀錄流水紀錄。
5.支付紀錄表,應該是每發起一次支付,就要生成一條支付紀錄。同一個訂單對應多個支付紀錄。

3,check_sign欄位生成

  /**
     * @param $walletInfo
     */
    public function updateSign($walletInfo)
    {
        unset($walletInfo['check_sign']);
        unset($walletInfo['update_at']);
        $walletCheckKey = config('bill.walletCheckKey');
        $checkSign = $this->encode_HMAC($walletInfo, $walletCheckKey);

        $update['check_sign'] = $checkSign;
        $this->walletRepository->saveTransaction($walletInfo['id'], $update);
    }

    /**
     * 檢查sign
     *
     * @param $walletInfo
     *
     * @return bool
     */
    public function walletSignCheck($walletInfo)
    {
        $walletCheckKey = config('bill.walletCheckKey');
        $check = false;
        $checkSignDB = $walletInfo['check_sign'];

        unset($walletInfo['check_sign']);
        unset($walletInfo['update_at']);

        $checkSign = $this->encode_HMAC($walletInfo, $walletCheckKey);
        if ($checkSignDB == $checkSign) {
            $check = true;
        }
        return $check;
    }

    /** * sign生成
    * * @param $walletInfo
    * * @return bool 
    */
    protected function encode_HMAC($data, $walletCheckKey)
    {
        if (empty($data) || !is_array($data)) {
            _writeBizException(json_encode([$data]), __FUNCTION__, class_basename(__CLASS__));
        }
        ksort($data);
        $str = "";
        foreach ($data as $k => $v) {
            $str = $str . "&" . $k . "=" . $v;
        }
        $str = trim($str, "&");
        $str = $str . "&key=" . $walletCheckKey;
        $re = strtoupper(hash_hmac("sha256", $str, $walletCheckKey));
        return $re;
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章