如何整合支付宝到Rails应用中

本文将简单介绍如何接入支付宝到你的Rails4应用中。本人已知的一些支持接入支付宝的gempay_fuactivemerchant_patch_for_china等等。但是由于RoR目前国内仍然比较非主流,这2个gem并没有维护的非常好。本文将介绍如何自己写一个简单接入支付宝的Rails helper。(用到了很多pay_fuactivemerchant_patch_for_china中的源代码)

申请支付宝商家服务

在技术集成之前,首先需要申请支付宝商家服务产品,获取PIDKey。现在支付宝商家服务最常见的产品包括双功能收款平台商双功能收款。二者的主要区别在于双功能收款的买家只能对你进行付款,平台商双功能收款能让买家直接对入驻你平台的卖家进行付款。审核通过后,就能在这里找到相关的技术文档,进行技术集成了。

支付宝接口调用流程

当用户在你的应用上完成订单,点击例如去支付之类的按钮时,按照支付宝文档中的参数要求生成请求参数,利用Railsredirect_to函数,让用户跳转到支付宝网关的url+请求参数。你应用这边的事情就结束了,一切顺利的话,用户跳转到支付宝网关,登录支付宝账号并支付,一笔交易就完成啦!需要注意的是,请求参数中提供了2个可选参数return_urlnotify_url。分别是支付宝交易处理完成后,同步和异步反调请求地址。也就是说,你的应用需要提供2个url给支付宝调用,用来通知你交易的状态。如果这2个参数为空,支付宝不会进行反调。所有交易记录都可以在支付宝账号中查看。

关于生成签名

你发送给支付宝网关的请求参数,支付宝会通过验证signsign_type这2个必填参数来判断请求的合法性。但是如何生成signsign_type呢?举个例子,假如说现在除了signsign_type我只要传a=1b=2c=3这三个参数给支付宝网关。那么sign的值应该如下。(注意:参数需按照字母顺序排序。示例使用sign_type为MD5,其他还有RSA等等的验证方式。key就是之前申请支付宝商家服务时,支付宝给你的一个密钥。)

1
2
sign      = MD5('a=1&b=2&c=3' + key )
sign_type = 'MD5'

helper函数

首先在helper文件夹下建alipay文件夹。这里比较随意,你也可以不建这个文件夹。在alipay下建立gateway_helper.rb。名字可以随意,_helper.rb后缀能保证默认配置下,Rails读取这个文件。

1
2
3
4
5
6
D:.
├─app
│  ├─assets
│  ├─controllers
│  ├─helpers
│  │  └─alipay

生成请求参数

gateway_helper.rb中添加如下代码。(其中用到了很多pay_fuactivemerchant_patch_for_china中的源代码)

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
67
68
69
70
71
# encoding: utf-8
require 'digest/md5'

module Alipay #:nodoc:
  module GatewayHelper #:nodoc:

  # 直接叫这个函数,放入参数,就能实现跳转啦
    def alipay_purchase(options={})
    # 准备请求参数
      query = prep_params(options)
    # 阿里网关url + 请求参数
      alipay_url = 'https://mapi.alipay.com/gateway.do?' + querify_esc(query)
    # 页面跳转
      redirect_to alipay_url
    end

    private

    def prep_params(options={})
      query_params = {
        # ===> 配置信息 <===
      #   隐藏隐私信息在配置文件中,配置文件不要用SCM管理。
        :partner => ALIPAY_CONFIG[Rails.env]['pid'],
        :"_input_charset" => 'utf-8',
      # return_url和notify_url暂时不用
        # :notify_url => options[:notify_url],
        # :return_url => options[:return_url],
      # 支付宝商家服务类型,这里是双功能收款。
        :service => 'trade_create_by_buyer',
        # ===> 交易信息 <===
      #   你系统中的订单号,交易成功的订单号再次请求会失败
        :out_trade_no => options[:out_trade_no],
        :price => options[:price],
        :quantity => 1,
        # :seller_email => SELLER_EMAIL,
        :seller_id => ALIPAY_CONFIG[Rails.env]['pid'],
        :payment_type => 1,
      #   题目(你网站名称)
        :subject => options[:subject],
      #   商品清单(我个人理解。。。)
        :body => options[:body],
        # ===> 物流 <===
        :logistics_type => 'POST',
        :logistics_fee => 0,
        :logistics_payment => 'BUYER_PAY',
        # ===> 用户信息 <=== 
        :receive_name => options[:receive_name],
        :receive_address => options[:receive_address],
        :receive_mobile => options[:receive_mobile],
        :receive_zip => options[:receive_zip],
        :receive_phone => options[:receive_phone],
      }
      query_params = Hash[query_params.sort]
    # sign = MD5('a=1&b=2&c=3' + key )
      query_params[:sign] = Digest::MD5.hexdigest(querify(query_params) + ALIPAY_CONFIG[Rails.env]['key'])
      query_params[:sign_type] = 'MD5'
      query_params
    end

  def querify(query_hash)
    # 把哈希变成'a=1&b=2&c=3',验证md5用
      query_hash.map {|key, value| "#{key}=#{value.to_s}" }.join("&")
    end

    def querify_esc(query_hash)
      # 把哈希变成'a=1&b=2&c=3',跳转用
      query_hash.map {|key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
    end    

  end
end

这样就okay了。下面将简单示范参考调用方法。

在routes.rb中 (参考用法)

1
2
3
resources :orders do
  post 'pay', on: :member
end

在View中 (参考用法)

1
<%= link_to pay_order_path(@order), class: 'btn-u', method: :post do %>去支付<% end %> 

在Controller中 (参考用法)

1
2
3
4
5
6
7
8
9
10
11
12
13
def pay
  alipay_purchase(
      :out_trade_no => @order.id,
      :price => @order.total_price,
      :subject => '某某网',
      :body => @order.products,
      :receive_name => @order.name,
      :receive_address => @order.address,
      :receive_mobile => @order.cell_phone,
      :receive_zip => @order.zip,
      :receive_phone => @order.phone
  )
end

附录

  • 平台商双功能收款接入文档

Comments