#encoding: utf-8 require 'base64' class TrainingsController < ApplicationController wechat_responder skip_before_filter :check_if_login_required skip_before_filter :verify_signature, except: [:auth, :auth_callback, :pay_callback] ROOT_URL = ENV["wechat_url"] || "#{Setting.protocol}://#{Setting.host_name}" before_filter :authenticate, except: [:auth, :auth_callback, :pay_callback] before_filter :check_training_type, only: [:enroll] before_filter :check_current_training, only: [:show, :update, :pay, :pay_js, :update_payinfo, :result] layout 'base_trainings' def show @training = current_training if @training.training_payinfo.present? && params[:disable_redirect].blank? redirect_to result_training_path(id: friendly_id) return end render 'trainingsInfo' end def enroll @training = current_training || Training.new if @training.training_payinfo.present? && params[:disable_redirect].blank? redirect_to result_training_path(id: friendly_id) return end end def pay @training = current_training # 防止重复支付,对于已支付过的,不应该再到这个页来 if @training.payed? redirect_to result_training_path(id: friendly_id) return end if @training.training_payinfo.blank? @training.build_training_payinfo @training.training_payinfo.fee = @training.registration_fee @training.training_payinfo.pay_type = params[:pay_type].presence || 3 end end def pay_callback # {"xml"=>{"appid"=>"wx8c5bd82a8a584cf6", "bank_type"=>"CFT", "cash_fee"=>"1", "fee_type"=>"CNY", # "is_subscribe"=>"Y", "mch_id"=>"1516303811", "nonce_str"=>"c462b28237d5e70261c3171319d86bf5", # "openid"=>"o5fSc02LFi7LEGCk_ckp6YbegFWs", "out_trade_no"=>"2018101714093870", # "result_code"=>"SUCCESS", "return_code"=>"SUCCESS", "sign"=>"155D16352B5F2287A32B3121175D8B0B", # "time_end"=>"20181017140944", "total_fee"=>"1", "trade_type"=>"JSAPI", # "transaction_id"=>"4200000166201810174468792543"}} logger.info "pay_callback #{params}" unless params["xml"] logger.error "pay_callback error: not found xml" end pay = WechatPay.find_by_out_trade_no(params[:xml]["out_trade_no"]) pay.update_attributes!(params[:xml]) training = Training.find(pay.training_id) training_payinfo = training.training_payinfo training_payinfo.update_attributes!(status: TrainingPayinfo::Status_Payed, out_trade_no: pay.out_trade_no) # 报名成功发送短信 =begin begin Trustie::Sms.send(mobile: training.phone, send_type: "training_pay", user_name: training.name) rescue => e logger.error("pay_callback 短信发送失败: #{e}") end =end render :xml => '' ' ' '' end def result @training = current_training if @training.training_payinfo.blank? redirect_to training_path(id: friendly_id) return end end def create @training = Training.new @training.training_type = @training_type @training.openid = session[:wechat_open_id] save_training redirect_to training_path(id: friendly_id, disable_redirect: true) end def update @training = current_training save_training redirect_to training_path(id: friendly_id, disable_redirect: true) end # 对应两种情况的信息提交 # 1. 稍后支付 # 2. 立刻支付 # 采用ajax调用方式,返回支付参数 def update_payinfo @training = current_training training_info = @training.training_payinfo || @training.build_training_payinfo training_info.assign_attributes(params) # 已支付不能修改人数 if training_info.not_payed? training_info.num = params[:enlistNum].to_i < 1 ? 1 : params[:enlistNum].to_i training_info.fee = @training.registration_fee(training_info.num) end ActiveRecord::Base.transaction do if training_info.pay_type.to_i == TrainingPayinfo::PayType_bank attachment = nil attachment = Attachment.create!(file: params[:image], author: User.first) if params[:image] if attachment.blank? flash[:message] = '请先上传支付凭证' render 'pay' return end training_info.attachment = attachment end if training_info.not_payed? if training_info.pay_type.to_i == TrainingPayinfo::PayType_Wechat training_info.status = TrainingPayinfo::Status_None else training_info.status = TrainingPayinfo::Status_Wait end end training_info.save! if params[:js] == 'true' Rails.logger.info("### start wechat pay => fee: #{training_info.fee}") _pay_js(training_info.fee) else redirect_to result_training_path(id: friendly_id) end end end ### js function def update_picture @training = current_training js_function_call do attachment = Attachment.create!(file: params[:image], author: User.first) training_payinfo = @training.training_payinfo training_payinfo.attachment = attachment training_payinfo.status = TrainingPayinfo::Status_Wait if training_payinfo.status == TrainingPayinfo::Status_None training_payinfo.save! render json: {status: 0} end end def pay_js @training = current_training _pay_js(@training.training_payinfo.fee) end #js获取支付参数 def _pay_js(fee) @training = current_training js_function_call do out_trade_no = Wechat.pay.gen_trade_no #@training.training_payinfo.update_attribute(:out_trade_no, out_trade_no) # # # 写入wechat_pay付费表 WechatPay.create!(training_id: @training.id, out_trade_no: out_trade_no) render json: {status: 0, data: unifiedorder(out_trade_no, fee, @training.pay_order_title)} end end # 用于权限跳转 def auth state = params[:state] url = CGI.escape("#{ROOT_URL}/trainings/auth_callback?return_url=#{params[:return_url]}") authorize_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{url}&response_type=code&scope=snsapi_base&state=#{state}&connect_redirect=1#wechat_redirect" redirect_to authorize_url end def auth_callback path = Base64.urlsafe_decode64(params[:state]) open_id = get_openid_from_code(params[:code]) unless open_id render 'wechats/open_wechat', layout: nil and return end session[:wechat_open_id] = open_id redirect_to params[:return_url].present? ? params[:return_url] : '/' end def test end private def authenticate if Rails.env.development? session[:wechat_open_id] = "o5fSc0607iR3rp4-h_VnuBTp8CiM" end unless session[:wechat_open_id].present? redirect_to auth_trainings_path(return_url: CGI.escape(request.path)) end end def save_training @training.assign_attributes(params) @training.training_type = training_type if @training.training_type == 3 @training.research_field = params[:research_field].select(&:present?).uniq.join(",") end @training.save! end def friendly_id @friendly_id ||= params[:friendly_id].presence || params[:id] end def training_type @training_type ||= case friendly_id when 'aeee0601_2019' then 3 when 'ceeaa06_2019' then 4 end end def current_training @_current_training ||= begin Rails.logger.info("##########openid:#{session[:wechat_open_id]}, friendly_id: #{friendly_id}") return if training_type.blank? Training.where(openid: session[:wechat_open_id], training_type: training_type).first end end def check_training_type return if training_type.present? render_404 end def check_current_training if current_training.blank? if training_type.blank? render_404 return end redirect_to enroll_training_path(id: friendly_id) return end end def js_function_call begin yield if block_given? rescue => e render json: {status: 1, msg: e.message} end end def client_ip request.headers['X-Real-IP'] || request.remote_ip || "127.0.0.1" end def get_openid_from_code(code) openid = session[:wechat_open_id] unless openid if code #不能联系调两次web_access_token 否则会提示请在微信客户端打开次链接 info = wechat.web_access_token(code) openid = info["openid"] access_token = info["access_token"] if access_token session[:access_token] = access_token end refresh_token = info["refresh_token"] if refresh_token session[:refresh_token] = refresh_token end end end if openid session[:wechat_open_id] = openid end return openid end def unifiedorder(out_trade_no, fee, title) @config = {} output = Wechat.pay.unifiedorder(title, (fee * 100).to_i, session[:wechat_open_id], client_ip, out_trade_no) data = output.fetch("xml") if data.nil? raise "获取微信统一单错误" end if data["return_code"] != 'SUCCESS' raise "获取微信统一单错误:#{data["return_msg"]}" end @config[:appid] = data["appid"] @config[:nonce] = data["nonce_str"] @config[:prepay_id] = data["prepay_id"] @config[:time] = Time.now.to_i info = { appId: @config[:appid], timeStamp: @config[:time], nonceStr: @config[:nonce], package: "prepay_id=#{@config[:prepay_id]}", signType: 'MD5' } @config[:sign] = Wechat.pay.sign(info) @config end end