Day 0xE - 建立订单纪录到资料库

0x1 前言

目前还没有画面可以看或操作,所以单就资料流的部分先写进资料库,
而建立订单目前也是先塞变数进去,所以每次都会是新单。
今天的目标就是写进资料库,并产生一个简单的表格来看(最阳春的那种)

0x2 处理流程

主要记录的有几个栏位

  • 请求
    1. OrderNo - 订单编号
    2. Amonut - 订单金额
    3. PrdtName - 顾客名称
    4. PayType - 付款方式
      虚拟 ATM
    5. ExpireDate - 订单逾期日期
      信用卡
    6. AutoBilling - 是否自动请款
    7. ExpBillingDays - 自动请款天数
  • 回应
    9. TSNo - 交易编号
    10. Status - 处理状态
    11. Description - 回应讯息
    虚拟 ATM
    12. AtmPayNo - 虚拟帐号
    13. WebAtmURL - Web ATM 网址
    14. OtpURL - 一次性密码网址
    信用卡
    15. CardPayURL - 信用卡付款网址

之前规划的栏位已经有了 [订单编号, 订单总金额, 付款方式, 订单状态, 订单过期日期],
还缺少 [顾客名称, 是否自动请款, 自动请款天数, 交易编号, 回应讯息, 虚拟帐号, Web ATM 网址, 一次性密码网址, 信用卡付款网址]

  • 建立 migration
>  php artisan make:migration --table=sale_orders update_sale_order
  • 更改 migration 档案
// database/migrations/[current_date_time]_update_sale_order.php
public function up()
{
    Schema::table('sale_orders', function (Blueprint $table) {
        $table->string('prdt_name', 50)->comment('顾客名称');
        $table->string('auto_billing', 1)->nullable()->comment('是否自动请款');
        $table->tinyInteger('exp_billing_days')->nullable()->comment('自动请款天数');
        $table->string('ts_no', 20)->nullable(false)->comment('交易编号');
        $table->string('description', 50)->nullable()->comment('回应讯息');
        $table->string('atm_pay_no', 14)->nullable()->comment('虚拟帐号');
        $table->string('web_atm_url', 255)->nullable()->comment('Web ATM 网址');
        $table->string('otp_url', 255)->nullable()->comment('一次性密码网址');
        $table->string('card_pay_url', 255)->nullable()->comment('信用卡付款网址');
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    Schema::table('sale_orders', function (Blueprint $table) {
        $table->dropColumn(['prdt_name', 'auto_billing', 'exp_billing_days', 'ts_no', 'description', 'atm_pay_no', 'web_atm_url', 'otp_url', 'card_pay_url']);
    });
}
  • 修正 付款时间能为空值
// database/migrations/[create_date_time]_create_sale_orders_table.php
    ...
    $table->dateTime('pay_datetime')->nullable()->comment('付款时间');
    ...
  • 执行 migrate,查看 table 栏位就能看到已经变更了(fresh 会把所有 table 都移除後,重新执行所有 migrate)
>  php artisan migrate:fresh

https://ithelp.ithome.com.tw/upload/images/20210924/20141805A6C5AfB4ss.png
(此工具为 PhpStorm 的 Database,在开发的时候能检查SQL语法对应的表格或栏位,非工商 XD)

  • 增加 model fillable 阵列,让 orm 可以写
// app/Models/sale_order.php
protected $fillable = [
    ...
    'prdt_name',
    'auto_billing',
    'exp_billing_days',
    'ts_no',
    'description',
    'atm_pay_no',
    'web_atm_url',
    'otp_url',
    'card_pay_url'
];
  • 接下来 改写 create_order 函数,让资料可以写进资料库
public function create_order(Request $request)
{
    $sinopac = $this->initSinopac();
    $order_no = date('YmdHis');
    $amount = random_int(4000, 10000);
    $pay_type = ($request->get('credit', '') === 'Y') ? 'C' : 'A';
    $expire_date = date('Ymd', time() + 604800);

    $data = [
        'ShopNo'        => $sinopac->shop_no,
        'OrderNo'       => $order_no,
        'Amount'        => $amount . '00',
        'CurrencyID'    => 'TWD',
        'PrdtName'      => '大河',
        'ReturnURL'     => 'http://10.11.22.113:8803/QPay.ApiClient-Sandbox/Store/Return',
        'BackendURL'    => 'https://sandbox.sinopac.com/funBIZ.ApiClient/AutoPush/PushSuccess',
        'PayType'       => $pay_type,
        'ATMParam'      => [
            'ExpireDate' => $expire_date,
        ],
    ];

    $data = $sinopac->requestDataset('OrderCreate', $data);
    $message = $sinopac->callApi('https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order', $data);

    $reply_nonce = $message['Nonce'] | '';
    if (!$reply_nonce) {
        $msg = 'Reply message haven\'t Nonce';
        Log::error($msg , $message);
        throw new \Exception($msg);
    }

    // 1. nonce 计算 iv
    $iv = $sinopac->calculateIv($reply_nonce);
    // 2. 计算 hash_id (AES key)
    $hash_id = $sinopac->calcHashId();
    // 3. message 解密
    $decrypt_message = $sinopac->decryptMessage($message['Message'], $hash_id, $iv);
    // 4. 验证 sign
    $sign = $sinopac->generateSign($decrypt_message, $reply_nonce, $hash_id);

    if (!($sign === $message['Sign'])) {
        return ['msg' => '验证错误,内文签章不同'];
    }

    // 这里的 – 是 \xE2  不是 \x2D
    $description = explode(' – ', $decrypt_message['Description']);
    if ($description[0] !== 'S0000') {
        Log::alert('订单未建立成功', $decrypt_message);
    }

    $dataset = [
        'customer_id'       => 0,
        'order_no'          => $order_no,
        'total'             => $amount,
        'pay_type'          => $pay_type,
        'expire_date'       => $expire_date,
        'ts_no'             => $decrypt_message['TSNo'],
        'status'            => $decrypt_message['Status'],
        'description'       => $decrypt_message['Description'],
        'mailing_address'   => '',
        'prdt_name'         => '大河',
    ];
    if ($pay_type === 'C') {
        // 信用卡
        $card_param = $decrypt_message['CardParam'];
        $dataset = array_merge($dataset, [
            'card_pay_url'  => $card_param['CardPayURL'],
        ]);
    } else {
        // 虚拟帐号
        $atm_param = $decrypt_message['ATMParam'];
        $dataset = array_merge($dataset, [
            'atm_pay_no'    => $atm_param['AtmPayNo'],
            'web_atm_url'   => $atm_param['WebAtmURL'],
            'otp_url'       => $atm_param['OtpURL'],
        ]);
    }
    $id = sale_order::create($dataset)->id;

    return [
        'dataset' => $dataset,
        'id' => $id
    ];
}
  • 建立 Web controller,然後资料全部倒出来啦
> php .\artisan make:controller Web
// app/Http/Controllers/Web.php
public function orders()
{
    return view('orders', [
        'dataset' => sale_order::all()->toArray()
    ]);
}
  • resources/views 建立 orders.blade.php
// resources/views/orders.blade.php
<style>
    table {
        width: 100%;
        text-align: center;
    }

    thead > tr, tr:hover {
        background: #1a202c30;
    }
</style>
<table>
    <thead>
        <tr>
            <th>#</th>
            <th>订单编号</th>
            <th>订单金额</th>
            <th>订单类型</th>
            <th>客户名称</th>
            <th>客户住址</th>
            <th>处理状态</th>
            <th>银行回覆内容</th>
            <th>付款期限</th>
            <th>建立日期</th>
            <th>更新日期</th>
        </tr>
    </thead>
    <tbody>
        @foreach($dataset as $index => $line)
            <tr>
                <td>{{ $index + 1 }}</td>
                <td>{{ $line['order_no'] }}</td>
                <td>{{ $line['total'] }}</td>
                <td>{{ $line['pay_type'] }}</td>
                <td>{{ $line['prdt_name'] }}</td>
                <td>{{ $line['mailing_address'] }}</td>
                <td>{{ $line['status'] }}</td>
                <td>{{ $line['description'] }}</td>
                <td>{{ $line['expire_date'] }}</td>
                <td>{{ $line['created_at'] }}</td>
                <td>{{ $line['updated_at'] }}</td>
            </tr>
        @endforeach
    </tbody>
</table>
  • 建立看订单的 route
// routes/web.php
Route::get('/orders', [Web::class, 'orders']);

OK~来看个成果
https://ithelp.ithome.com.tw/upload/images/20210924/20141805HYOEYbrdce.png

0x3 今日结语

差点赶不上发文阿,要改的bug好多阿,有点怀疑自己为什麽要参赛了/images/emoticon/emoticon02.gif
明天要把让自己轻松一些,简单看一下回应的网页长怎麽样好了 ('web_atm_url', 'otp_url', 'card_pay_url')


<<:  [Day 15] 小整理

>>:  Day 9: Find the Good Job!

Day 5 - 部署 Home Lab 网路 - 下

那昨天安装好路由器後,我们要来架设一下 VPN。 在此之前,我们今天先来说明一下 VPN 是什麽。 ...

视觉化当日趋势图(1)-client端架设&&工具篇

昨天我们完成了用Flask撰写ticks API, API端好了之後,接下来我们要开始架设我们的cl...

Angular 深入浅出三十天:表单与测试 Day22 - 把 Cypress 变成 TypeScript 版

平常都用惯 TypeScript 版的 Cypress,但这两天都用 JavaScript 在写测...

有时差的我如何跟团队协作

由於目前人在美东,与台湾的时差刚好 12hr,完全可以说出:「当我在睡觉时,台湾的大家都还在认真工作...

Gulp npm install 中的 --save 与 --save-dev 差异 DAY94

npm install --save (产品用) npm install --save-dev (开...