支付系统与 dashboard

80学院第八次课

大纲

  • 任务回顾
  • 支付系统设计要点
  • 前后台 dashboard 设计要点

本周任务回顾

支付系统设计

  • gem: aasm( 状态机管理 )
  • 积分的改变
  • 互斥锁的应用( 可选, 可以讲讲数据库锁的应用与使用场景 )

支付系统核心代码(1)

# /app/models/order.rb

class Order < ApplicationRecord
  aasm do
    state :submitted, :initial => true
    state :paid
    state :cancelled
    event :pay, after: [:payback_writer, :payback_shared_by_user] do
      transitions from: [:submitted, :cancelled], to: :paid
    end
    event :cancel do
      transitions from: :submitted, to: :cancelled
    end
  end
end

支付系统核心代码(2)

# app/controllers/reader/orders_controller.rb

  def notify
    result = Hash.from_xml(request.body.read)["xml"]
    if WxPay::Sign.verify?(result)
      trade_no = result["out_trade_no"]
      @order = Order.find_by(trade_no: trade_no)

      @order.lock!
      if( @order.paid? )
        order_token_for_order_id
        render :xml => {return_code: "SUCCESS"}.to_xml(root: 'xml', dasherize: false)
        return
      end

      if( @order.may_pay? )
        @order.pay!
        @order.send_cable_notify
        order_token_for_order_id
        render :xml => {return_code: "SUCCESS"}.to_xml(root: 'xml', dasherize: false)
      else
        logger.warn "操作订单失败: order = #{@order}, notify result=#{result}"
        render :xml => {return_code: "FAIL", return_msg: "操作订单失败"}.to_xml(root: 'xml', dasherize: false)
      end
    else
      render :xml => {return_code: "FAIL", return_msg: "签名失败"}.to_xml(root: 'xml', dasherize: false)
    end
  end

支付系统核心代码(3)

# app/controllers/reader/orders_controller.rb

  def pay
    @hash = WxPay::Service.invoke_unifiedorder(tmp)
    if @hash["return_code"] != "SUCCESS"
      logger.warn " WxPay::Service.invoke_unifiedorder return error: #{@hash}"
      render js: "alert('error call WxPay::Service.invoke_unifiedorder')"
      return
    end

    if params[:qrcode].present?
      @qrcode_url = @hash["code_url"]
      render 'pay_qrcode'
      return
    end

    @js_pay_hash = {
      appId: WxPay.appid,
      timeStamp: Time.zone.now.to_i.to_s,
      nonceStr: SecureRandom.uuid.tr('-', ''),
      package: "prepay_id=#{@hash["prepay_id"]}",
      signType: "MD5",
    }
    @js_pay_hash["paySign"] = WxPay::Sign.generate(@js_pay_hash)
    logger.debug @js_pay_hash
  end

前后台 dashboard 设计

  • reader: layout 'reader_base'
  • writer: layout 'writer_base'
  • admin: layout 'admin_base'
  • 使用带命名空间的 controller
  • 使用分离的 layout
  • 使用带命名空间的 route

核心代码(1)

# config/routes.rb

namespace :admin do
  root 'dashboard#index'
end

namespace :reader do
  root 'dashboard#index'
end

namespace :writer do
  root 'dashboard#index'
end

核心代码(2)

# app/controllers/reader/application_controller.rb

class Reader::ApplicationController < ApplicationController
  before_action :authenticate_user!
  layout 'wechat_base'
end


# app/controllers/writer/application_controller.rb

class Writer::ApplicationController < ApplicationController
  before_action :authenticate_user!
  layout 'writer_base'
end

核心代码(3)

# app/views/layouts/reader_base.html.slim

# app/views/layouts/writer_base.html.slim

# app/views/layouts/admin_base.html.slim

doctype html
html
  head
    meta charset='utf-8'
    meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
    meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"
    meta name="renderer" content="webkit"
    meta http-equiv="cleartype" content="on"
    meta name="HandheldFriendly" content="True"
    meta name="MobileOptimized" content="320"
    meta name="format-detection" content="telephone=no"
    - if content_for?(:title)
      = yield(:title)
    - else
      title 八十二十 - 有价值的文章分享工具
    = csrf_meta_tags
    = action_cable_meta_tag
    = content_for?(:head) ? yield(:head) : ''
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'ga', 'data-turbolinks-track': 'reload'
    = javascript_include_tag "https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
    = favicon_link_tag "favicon.png"
  body
    main
      == render 'layouts/flash'
      .container
        == yield

Break time

任务安排

  • 订单系统实现
  • 积分系统实现
  • 支付购买功能

问题答疑与结对编程

Thx

80academy-lession-8

By Li Yafei

80academy-lession-8

  • 2,669