乐者为王

Do one thing, and do it well.

在Rails应用中集成支付宝

注意:notify_url为服务器通知,支付宝可以保证99.9999%的通知到达率,前提是你的网络通畅,在notify_url中可以做对数据库的业务操作。return_url中可以做数据库的更新也可以做显示。第一次交易状态改变(即时到帐中的交易完成的交易状态)时,支付宝发起的通知的时间与返回页自动跳转回的时间近乎同时。

支付处理代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class OrdersController < ApplicationController
  before_filter :login_required

  # place order
  def place_order
    parameters = {
      'service' => 'create_direct_pay_by_user',
      'partner' => ALIPAY_ACCOUNT,
      'seller_email' => ALIPAY_EMAIL,
      'out_trade_no' => @order.out_trade_no,
      'subject' => 'payment subject',
      'body' => 'payment body',
      'price' => @order.price.to_s,
      'quantity' => @order.quantity.to_s,
      'payment_type' => '1',
      '_input_charset' => 'utf-8',
      'notify_url' => url_for(:only_path => false, :action => 'notify'),
      'return_url' => url_for(:only_path => false, :action => 'done')
    }

    # 即时到帐中交易状态为“等待买家付款”的状态默认是不会发送通知的,自己手动设置一下
    @order.status = 'WAIT_BUYER_PAY'
    @order.user = current_user
    @order.save

    values = {}
    # 支付宝要求传递的参数必须要按照首字母的顺序传递,所以这里要sort
    parameters.keys.sort.each do |k|
      values[k] = parameters[k];
    end

    # 一定要先unescape后再生成sign,否则支付宝会报ILLEGAL SIGN
    sign = Digest::MD5.hexdigest(CGI.unescape(values.to_query) + ALIPAY_KEY)
    gateway = 'https://www.alipay.com/cooperate/gateway.do?'
    redirect_to gateway + values.to_query + '&sign=' + sign + '&sign_type=MD5'
  end

  # 返回success或fail。如果返回fail,支付宝会每隔一段时间就自动调用notify_url通信接口
  def notify
    render :text => 'success'
  end

  def done
    if verify_sign
      order = Order.find_by_out_trade_no(params[:out_trade_no])
      # 支付宝即时到帐接口只有一种交易状态,就是“交易成功”,更新一下
      order.update_attributes(params[:trade_status])
      render :text => 'Payment successful'
    else
      render :text => 'Alipay Error: ILLEGAL_SIGN'
    end
  end

protected
  def verify_sign
    params.delete("sign_type")
    sign = params.delete("sign")

    values = {}
    params.keys.sort.each do |k|
      values[k] = params[k];
    end

    sign.downcase == Digest::MD5.hexdigest(CGI.unescape(values.to_query) + ALIPAY_KEY)
  end
end

Comments