Skip to content
lanrion edited this page Dec 4, 2015 · 3 revisions

由于轮子 https://github.com/jasl/wx_pay.git 使用比较良好,weixin_authorize 目前暂不提供支付接口集成。 下面是使用JSAPI实现支付的使用说明:

安装

Gemfile:

gem 'wx_pay', git: 'https://github.com/jasl/wx_pay.git'

your-app/config/initializers/wx_pay.rb:

# required
# WxPay.appid = Rails.application.secrets.app_id
WxPay.appid = "wxf5xxx"
WxPay.key = "xxxx"
WxPay.mch_id = "123123"
# optional - configurations for RestClient timeout, etc.
WxPay.extra_rest_client_options = {timeout: 2, open_timeout: 3}
# params = {
#   body: '测试商品',
#   out_trade_no: 'test003',
#   total_fee: 1,
#   spbill_create_ip: '127.0.0.1',
#   notify_url: ERB::Util.url_encode('http://making.dev/notify'),
#   trade_type: 'JSAPI',
#   nonce_str: "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
#   openid: "oah0RuOMkP2Kurp48bweSoaDqoEM"
# }

统一下单,生成预支付ID

order.rb:

  # 只有当微信支付时使用到,订单一旦确认(confirm),即进行获取
  # {remote_ip: request.ip}
  def set_prepay_id(options={})
    return true if Rails.env.development?
    return true if is_prepay_id_valid?
    # 重新更新支付流水号
    self.pay_serial_number = "#{number}-#{Time.current.to_i}"
    unifiedorder = {
      body: "#{SITE_NAME}-#{number}",
      out_trade_no: pay_serial_number,
      total_fee: (total_price * 100).to_i, # 需要转换为分
      spbill_create_ip: options[:remote_ip] || '127.0.0.1',
      notify_url: wx_notify_url,
      trade_type: "JSAPI",
      nonce_str: SecureRandom.hex,
      openid: user.openid
     }
     Rails.logger.debug("unifiedorder_params: #{unifiedorder}")
     res = WxPay::Service.invoke_unifiedorder(unifiedorder)
     if res.success?
      self.prepay_id = res["prepay_id"]
      self.pre_pay_id_expired_at = Time.current + 2.hours
      Rails.logger.debug("set prepay_id: #{self.prepay_id}")
      self.save(validate: false)
     else
      Rails.logger.debug("set prepay_id fail: #{res}")
      false
     end
  end

  # {remote_ip: request.ip}
  def get_prepay_id(options={})
    set_prepay_id(options) if !is_prepay_id_valid?
    prepay_id
  end

  def wx_notify_url
    "#{Rails.application.secrets.host_url}pay_notify/weixin_notify"
  end

  # 总价
  def total_price
    self.item_total.to_f - self.promo_total.to_f
  end

  # 判断是否有效
  def is_prepay_id_valid?
    prepay_id.present? && Time.current.to_i <= pre_pay_id_expired_at.to_i
  end

调起支付

orders_controller.rb:

  def show
    @cash_voucher = @order.cash_voucher
    @pay_p = {
      appId: Rails.application.secrets.app_id,
      timeStamp: Time.now.to_i.to_s,
      nonceStr: SecureRandom.hex,
      package: "prepay_id=#{@order.get_prepay_id(remote_ip: request.ip)}",
      signType: "MD5"
    }
    @pay_sign = WxPay::Sign.generate(@pay_p)
  end

views/orders/show.html.erb: 此处需要使用到 JS SDK的API,请查看: https://github.com/lanrion/weixin_authorize/wiki/js-sdk 用户进入这个页面时,如果预支付ID无效则重新进行获取,点击“立即支付”时,会发起支付。

.....
.....
<% if [email protected]? && [email protected]? %>
  <a href="javascript:;" class="to_pay btn btn-small">立即支付</a>
<% end %>
<%= content_for :javascript do %>
  <script type="text/javascript">
    var order_id = "<%= @order.id %>";
    $(".to_pay").click(function(){
      wx.chooseWXPay({
        "timestamp": "<%= @pay_p[:timeStamp] %>",
        "nonceStr": "<%= @pay_p[:nonceStr] %>",
        "package": "<%= @pay_p[:package] %>",
        "signType": "<%= @pay_p[:signType] %>",
        "paySign": "<%= @pay_sign %>", // 支付签名
        success: function (res) {
          window.location.href = "/orders/" + order_id + "/pay_success";
        }
      });
    });
  </script>
<% end %>

接收支付回调与警报

routes.rb:

  resource :pay_notify, only: [] do
    collection do
      post :weixin_notify
      post :weixin_exception_notify
    end
  end

controllers:

class PayNotifiesController < ActionController::Base
  # 支付异步通知
  def weixin_notify
    result = Hash.from_xml(request.body.read)["xml"]
     Rails.logger.debug("weixin notify: #{result}")
    if WxPay::Sign.verify?(result)
      pay_serial_number = result["out_trade_no"]
      order = Order.find_by(pay_serial_number: pay_serial_number)
      order.paid_amount = result["total_fee"]
      order.state = "paid"
      order.pay_logs.new(
        pay_type: "weixin",
        trade_type: result[:trade_type],
        log: result
      )
      order.save(validate: false)
      # 支付成功后,减库存
      Rails.logger.debug("支付成功后,减库存")
      render xml: {
        return_code: "SUCCESS"
      }.to_xml(root: 'xml', dasherize: false)
    else
      render xml: {
        return_code: "FAIL",
        return_msg: "签名失败"
      }.to_xml(root: 'xml', dasherize: false)
    end
  end

  # 接收 报警
  # http://www.cnblogs.com/txw1958/p/weixin-payment-solution.html
  def weixin_exception_notify
    result = Hash.from_xml(request.body.read)["xml"]
    ServiceNotify.create(service_type: "weixin", content: result)
  end
end