cmb_pay 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 973b9cf0d5d2974860b0d7fff0cd75d570adc8d6
4
- data.tar.gz: 192ec7d563ace627bcd1e832d5691a8f76268ecf
3
+ metadata.gz: 7cb6e2e16c168adda66116084b9baee9885df88f
4
+ data.tar.gz: 538974a366f22d25f637d139d1a6422ff368f773
5
5
  SHA512:
6
- metadata.gz: 3cd712678f799430ce5c34b9382405448f837fe0752c28bfff61aea7b4f8af51043574c946c8f8231d024f9acf157c8753b50943e1a043932de9d2c70c1ea278
7
- data.tar.gz: 50472f3eabe787fe14e9a107404775d31b26108fa4a40fd56b77b80681e5ea4867480595a6312e733c804eb53e2629009ec73d7d7d7d39bc3616c5dc0f5def04
6
+ metadata.gz: 037d0f1f208ca53ba2bfb47d09c87f68d7ee5992e3e63e50d5d956b3bac73a8badf359839049803df0dcc441b94e8d2555ae5ff02715d1da03b5f1daacc06b3a
7
+ data.tar.gz: a8bfbee55dd68369f559f4f5c013cb330076046481f5948d3542f913791e2f760d68501a79b5f11fce929412084c75c5785cc8dfb19aed6e1afb0759ad6eaf9e
checksums.yaml.gz.sig CHANGED
Binary file
data/.codeclimate.yml ADDED
@@ -0,0 +1,18 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ exclude_fingerprints:
6
+ - 02bd778d4f4ff959546967a3bcaffc6e
7
+ config:
8
+ languages:
9
+ - ruby
10
+ fixme:
11
+ enabled: true
12
+ rubocop:
13
+ enabled: true
14
+ ratings:
15
+ paths:
16
+ - "**.rb"
17
+ exclude_paths:
18
+ - spec/
data/.rubocop.yml CHANGED
@@ -5,7 +5,7 @@ Documentation:
5
5
  Enabled: false
6
6
 
7
7
  Metrics/LineLength:
8
- Max: 150
8
+ Max: 300
9
9
 
10
10
  Metrics/AbcSize:
11
11
  Max: 57
@@ -21,3 +21,6 @@ Style/NumericLiterals:
21
21
 
22
22
  Style/AsciiComments:
23
23
  Enabled: false
24
+
25
+ Style/ParameterLists:
26
+ Max: 15
data/README.md CHANGED
@@ -3,6 +3,14 @@ CMB Pay [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]
3
3
 
4
4
  An unofficial cmb (China Merchants Bank) pay ruby gem, inspired from [alipay](https://github.com/chloerei/alipay), [wx_pay](https://github.com/jasl/wx_pay) and [cmbchina](https://github.com/yellong/cmbchina) a lot.
5
5
 
6
+ ## Feature
7
+
8
+ * Payment URL generation for Web, App and WAP(Mobile Web).
9
+ * CMB Bank notification payment callback parse and verify.
10
+ * Direct refund API.
11
+ * Single order query.
12
+ * Multi orders query by transact/merchant date/settled date.
13
+
6
14
  ## Installation
7
15
 
8
16
  Add this line to your application's Gemfile:
@@ -0,0 +1,85 @@
1
+ require 'rexml/streamlistener'
2
+
3
+ module CmbPay
4
+ class BillRecordsMessage
5
+ attr_reader :raw_http_response, :code, :error_message,
6
+ :query_loop_flag, :query_loop_pos, :bill_records
7
+ def initialize(http_response)
8
+ @raw_http_response = http_response
9
+ return unless http_response.code == 200
10
+ @bill_records = []
11
+
12
+ REXML::Document.parse_stream(http_response.body, self)
13
+ end
14
+
15
+ def tag_start(name, _attributes)
16
+ case name
17
+ when 'Head' then @in_head = true
18
+ when 'Body' then @in_body = true
19
+ when 'BllRecord' then @current_bill_record = {}
20
+ else
21
+ @current_element_name = name
22
+ end
23
+ end
24
+
25
+ def tag_end(name)
26
+ case name
27
+ when 'Head' then @in_head = false
28
+ when 'Body' then @in_body = false
29
+ when 'BllRecord' then @bill_records << @current_bill_record
30
+ end
31
+ end
32
+
33
+ def text(text)
34
+ if @in_head
35
+ case @current_element_name
36
+ when 'Code' then @code = text
37
+ when 'ErrMsg' then @error_message = text
38
+ end
39
+ elsif @in_body
40
+ case @current_element_name
41
+ # 续传标记(采用多次通讯方式续传时使用) 默认值为’N’,表示没有后续数据包,’Y’表示仍有后续的通讯包
42
+ when 'QryLopFlg' then @query_loop_flag = text
43
+ # 续传包请求数据
44
+ when 'QryLopBlk' then @query_loop_pos = text
45
+ # 商户定单号
46
+ when 'BillNo' then @current_bill_record[:bill_no] = text
47
+ # 商户日期
48
+ when 'MchDate' then @current_bill_record[:merchant_date] = text
49
+ # 结算日期
50
+ when 'StlDate' then @current_bill_record[:settled_date] = text
51
+ # 订单状态
52
+ when 'BillState' then @current_bill_record[:bill_state] = text
53
+ # 订单金额
54
+ when 'BillAmount' then @current_bill_record[:bill_amount] = text
55
+ # 手续费
56
+ when 'FeeAmount' then @current_bill_record[:fee_amount] = text
57
+ # 卡类型
58
+ when 'CardType' then @current_bill_record[:card_type] = text
59
+ # 交易流水号
60
+ when 'BillRfn' then @current_bill_record[:bill_ref_no] = text
61
+ # 实扣金额
62
+ when 'StlAmount' then @current_bill_record[:settled_amount] = text
63
+ # 优惠金额
64
+ when 'DecPayAmount' then @current_bill_record[:discount_pay_amount] = text
65
+ # 订单类型:A表示二维码支付订单,B表示普通订单
66
+ when 'BillType' then @current_bill_record[:bill_type] = text
67
+ # 如果订单类型为A,下述字段才存在
68
+ when 'Addressee' then @current_bill_record[:addressee] = text # 收货人姓名
69
+ when 'Country' then @current_bill_record[:country] = text # 国家
70
+ when 'Province' then @current_bill_record[:province] = text # 省份
71
+ when 'City' then @current_bill_record[:city] = text # 城市
72
+ when 'Address' then @current_bill_record[:address] = text # 街道地址
73
+ when 'Mobile' then @current_bill_record[:mobile] = text # 手机号
74
+ when 'Telephone' then @current_bill_record[:telephone] = text # 固定电话
75
+ when 'ZipCode' then @current_bill_record[:zipcode] = text # 邮编
76
+ when 'GoodsURL' then @current_bill_record[:goodsurl] = text # 商品详情链接
77
+ end
78
+ end
79
+ end
80
+
81
+ def succeed?
82
+ code.nil? && error_message.nil?
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,81 @@
1
+ module CmbPay
2
+ class PayMessage
3
+ attr_reader :succeed # 消息成功失败,成功为'Y',失败为'N'
4
+ attr_reader :co_no # 商户号,6位长数字,由银行在商户开户时确定
5
+ attr_reader :bill_no # 订单号(由支付命令送来);
6
+ attr_reader :amount # 实际支付金额(由支付命令送来)
7
+ attr_reader :date # 订单下单日期(由支付命令送来)
8
+ attr_reader :merchant_para # 商户自定义传递参数(由支付命令送来)
9
+ attr_reader :msg # 银行通知用户支付结构消息
10
+ attr_reader :discount_flag # 当前订单是否有优惠,Y:有优惠 N:无优惠。
11
+ attr_reader :discount_amt # 优惠金额,格式:xxxx.xx,当有优惠的时候,
12
+ # 实际用户支付的是amount - discount_amt的金额到商户账号。
13
+
14
+ attr_reader :branch_id # 分行号
15
+ attr_reader :bank_date # 银行主机交易日期
16
+ attr_reader :bank_serial_no # 银行流水号
17
+
18
+ attr_reader :signature # 通知命令签名
19
+
20
+ attr_reader :query_string # 原始的query_string
21
+
22
+ def initialize(query_string)
23
+ query_string = URI.encode_www_form(query_string) if query_string.is_a? Hash
24
+ @query_string = query_string
25
+
26
+ params = URI.decode_www_form(query_string).to_h
27
+
28
+ @succeed = params['Succeed']
29
+ @co_no = params['CoNo']
30
+ @bill_no = params['BillNo']
31
+ @amount = params['Amount']
32
+ @date = params['Date']
33
+ @merchant_para = params['MerchantPara']
34
+ @msg = params['Msg']
35
+ @discount_flag = params['DiscountFlag']
36
+ @discount_amt = params['DiscountAmt']
37
+
38
+ # 银行通知用户的支付结果消息。信息的前38个字符格式为:4位分行号+6位商户号+8位银行接受交易的日期+20位银行流水号;
39
+ # 可以利用交易日期+银行流水号+订单号对该订单进行结帐处理
40
+ msg = params['Msg'][0..37]
41
+ @branch_id = msg[0..3]
42
+ @co_no ||= msg[4..9]
43
+ @bank_date = msg[10..17]
44
+ @bank_serial_no = msg[18..37]
45
+
46
+ @signature = params['Signature']
47
+ end
48
+
49
+ def valid?
50
+ Sign::Sha1WithRsa.verify(query_string)
51
+ end
52
+
53
+ def succeed?
54
+ succeed == 'Y'
55
+ end
56
+
57
+ def discount?
58
+ discount_flag == 'Y'
59
+ end
60
+
61
+ def amount_cents
62
+ (amount.to_f * 100).to_i
63
+ end
64
+
65
+ def discount_amount_cents
66
+ (discount_amt.to_f * 100).to_i
67
+ end
68
+
69
+ def order_date
70
+ Date.strptime(date, '%Y%m%d')
71
+ end
72
+
73
+ def payment_date
74
+ Date.strptime(bank_date, '%Y%m%d')
75
+ end
76
+
77
+ def merchant_params
78
+ URI.decode_www_form(merchant_para.tr('|', '&')).to_h
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,34 @@
1
+ require 'rexml/document'
2
+
3
+ module CmbPay
4
+ class RefundOrderMessage
5
+ attr_reader :raw_http_response, :code, :error_message,
6
+ :refund_no, # 银行退款流水号
7
+ :bank_seq_no, # 银行流水号
8
+ :amount, # 退款金额
9
+ :date, # 银行交易日期YYYYMMDD
10
+ :time # 银行交易时间hhmmss
11
+
12
+ def initialize(http_response)
13
+ @raw_http_response = http_response
14
+ return unless http_response.code == 200
15
+
16
+ document_root = REXML::Document.new(http_response.body.to_s).root
17
+ head = document_root.elements['Head']
18
+ @code = head.elements['Code'].text
19
+ @error_message = head.elements['ErrMsg'].text
20
+ return unless succeed?
21
+
22
+ body = document_root.elements['Body']
23
+ @refund_no = body.elements['RefundNo'].text
24
+ @bank_seq_no = body.elements['BankSeqNo'].text
25
+ @amount = body.elements['Amount'].text
26
+ @date = body.elements['Date'].text
27
+ @time = body.elements['Time'].text
28
+ end
29
+
30
+ def succeed?
31
+ code.nil? && error_message.nil?
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require 'rexml/document'
2
+
3
+ module CmbPay
4
+ class SingleOrderMessage
5
+ attr_reader :raw_http_response, :code, :error_message,
6
+ :bill_no, # 定单号
7
+ :amount, # 实扣金额
8
+ :accept_date, # 受理日期
9
+ :accept_time, # 受理时间
10
+ :bill_amount, # 定单金额
11
+ :status, # 定单状态,0-已结帐,1-已撤销,2-部分结帐,4-未结帐,7-冻结交易-已经冻结金额已经全部结账 8-冻结交易,冻结金额只结帐了一部分
12
+ :card_type, # 卡类型:02:一卡通 03:信用卡
13
+ :fee, # 手续费
14
+ :merchant_para, # 商户自定义参数。里面的特殊字符已经转码
15
+ :card_no, # 卡号(部分数字用“*”掩盖)
16
+ :bank_seq_no # 银行流水号
17
+ def initialize(http_response)
18
+ @raw_http_response = http_response
19
+ return unless http_response.code == 200
20
+
21
+ document_root = REXML::Document.new(http_response.body.to_s).root
22
+ head = document_root.elements['Head']
23
+ @code = head.elements['Code'].text
24
+ @error_message = head.elements['ErrMsg'].text
25
+ return unless succeed?
26
+
27
+ body = document_root.elements['Body']
28
+ @bill_no = body.elements['BillNo'].text
29
+ @amount = body.elements['Amount'].text
30
+ @accept_date = body.elements['AcceptDate'].text
31
+ @accept_time = body.elements['AcceptTime'].text
32
+ @bill_amount = body.elements['BillAmount'].text
33
+ @status = body.elements['Status'].text
34
+ @card_type = body.elements['CardType'].text
35
+ @fee = body.elements['Fee'].text
36
+ @merchant_para = body.elements['MerchantPara'].text
37
+ @card_no = body.elements['CardNo'].text
38
+ @bank_seq_no = body.elements['BankSeqNo'].text
39
+ end
40
+
41
+ def succeed?
42
+ code.nil? && error_message.nil?
43
+ end
44
+ end
45
+ end
@@ -5,13 +5,15 @@ module CmbPay
5
5
  NP_BindCard: 'https://mobile.cmbchina.com/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
6
6
  PrePayEUserP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayEUserP',
7
7
  PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayC2',
8
- PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayWAP'
8
+ PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayWAP',
9
+ DirectRequestX: 'https://payment.ebank.cmbchina.com/netpayment/basehttp.dll?DirectRequestX'
9
10
  },
10
11
  test: {
11
12
  NP_BindCard: 'http://61.144.248.29:801/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
12
13
  PrePayEUserP: 'http://61.144.248.29:801/netpayment/BaseHttp.dll?PrePayEUserP',
13
14
  PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayC2',
14
- PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayWAP'
15
+ PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayWAP',
16
+ DirectRequestX: 'http://218.17.27.197/netpayment/basehttp.dll?DirectRequestX'
15
17
  }
16
18
  }.freeze
17
19
 
data/lib/cmb_pay/util.rb CHANGED
@@ -1,11 +1,24 @@
1
1
  module CmbPay
2
2
  module Util
3
+ MILLISECONDS_SINCE_UNIX_EPOCH = Time.new(2000, 1, 1).to_i * 1000
4
+
3
5
  def self.hex_to_binary(str)
4
- str.scan(/../).map(&:hex).map(&:chr).join
6
+ [str].pack('H*')
5
7
  end
6
8
 
7
9
  def self.binary_to_hex(s)
8
- s.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join.upcase
10
+ s.unpack('H*')[0].upcase
11
+ end
12
+
13
+ def self.cmb_timestamp(t: nil)
14
+ return t if t.is_a?(Integer)
15
+ t = Time.now if t.nil?
16
+ t.to_i * 1000 + (t.usec / 1000) - MILLISECONDS_SINCE_UNIX_EPOCH
17
+ end
18
+
19
+ # 招行定单号,6位或10位长数字,由商户系统生成,一天内不能重复
20
+ def self.cmb_bill_no(bill_no)
21
+ format('%010d', bill_no.to_i % 10_000_000_000)
9
22
  end
10
23
  end
11
24
  end
@@ -1,3 +1,3 @@
1
1
  module CmbPay
2
- VERSION = '0.0.3'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  end
data/lib/cmb_pay.rb CHANGED
@@ -1,25 +1,33 @@
1
1
  require 'date'
2
2
  require 'uri'
3
- require 'active_support/core_ext/hash'
3
+ require 'http' # https://github.com/httprb/http
4
4
  require 'cmb_pay/version'
5
5
  require 'cmb_pay/util'
6
6
  require 'cmb_pay/sign'
7
7
  require 'cmb_pay/merchant_code'
8
- require 'cmb_pay/message'
9
8
  require 'cmb_pay/service'
10
9
 
11
10
  module CmbPay
11
+ autoload(:PayMessage, File.expand_path('cmb_pay/message/pay_message', __dir__))
12
+ autoload(:RefundOrderMessage, File.expand_path('cmb_pay/message/refund_order_message', __dir__))
13
+ autoload(:SingleOrderMessage, File.expand_path('cmb_pay/message/single_order_message', __dir__))
14
+ autoload(:BillRecordsMessage, File.expand_path('cmb_pay/message/bill_records_message', __dir__))
15
+
12
16
  class << self
13
- attr_accessor :branch_id # 开户分行号
14
- attr_accessor :co_no # 支付商户号/收单商户号
15
- attr_accessor :co_key # 商户校验码,测试环境为空
16
- attr_accessor :mch_no # 协议商户企业编号,或者说是8位虚拟企业网银编号
17
- attr_accessor :expire_in_minutes # 会话有效时间
18
- attr_accessor :environment # 调用的招商银行支付环境,默认生产,测试填test
19
- attr_accessor :default_payee_id # 默认收款方的用户标识
17
+ attr_accessor :branch_id # 开户分行号
18
+ attr_accessor :co_no # 支付商户号/收单商户号
19
+ attr_accessor :co_key # 商户校验码/商户密钥,测试环境为空
20
+ attr_accessor :mch_no # 协议商户企业编号,或者说是8位虚拟企业网银编号
21
+ attr_accessor :operator # 操作员号,一般是9999
22
+ attr_accessor :operator_password # 操作员的密码,默认是支付商户号,但建议修改。
23
+ attr_accessor :expire_in_minutes # 会话有效时间
24
+ attr_accessor :environment # 调用的招商银行支付环境,默认生产,测试填test
25
+ attr_accessor :default_payee_id # 默认收款方的用户标识
20
26
  end
21
27
  @co_key = ''
22
28
  @mch_no = ''
29
+ @operator = '9999'
30
+ @operator_password = ''
23
31
  @expire_in_minutes = 30
24
32
  @environment = :production
25
33
 
@@ -79,19 +87,99 @@ module CmbPay
79
87
  options)
80
88
  end
81
89
 
82
- def self.cmb_pay_message(query_string)
83
- CmbPay::Message.new query_string
90
+ def self.pay_message(query_string)
91
+ PayMessage.new query_string
92
+ end
93
+
94
+ # 退款接口
95
+ def self.refund_no_dup(bill_no:, refund_no:, refund_amount_in_cents:, memo:,
96
+ bill_date: nil, operator: nil, operator_password: nil,
97
+ branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
98
+ refund_in_yuan, refund_in_cent = refund_amount_in_cents.to_i.divmod(100)
99
+ refund_amount = "#{refund_in_yuan}.#{format('%02d', refund_in_cent)}"
100
+ desc = memo.encode(xml: :text)
101
+ bill_date = Time.now.strftime('%Y%m%d') if bill_date.nil?
102
+ head_inner_xml = build_direct_request_x_head('Refund_No_Dup', branch_id, co_no, time_stamp,
103
+ with_operator: true, operator: operator, operator_password: operator_password)
104
+ body_inner_xml = "<Date>#{bill_date}</Date><BillNo>#{Util.cmb_bill_no(bill_no)}</BillNo><RefundNo>#{Util.cmb_bill_no(refund_no)}</RefundNo><Amount>#{refund_amount}</Amount><Desc>#{desc}</Desc>"
105
+ http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
106
+ RefundOrderMessage.new(http_response)
107
+ end
108
+
109
+ # 单笔定单查询接口
110
+ def self.query_single_order(bill_no:, trade_date: nil,
111
+ branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
112
+ trade_date = Time.now.strftime('%Y%m%d') if trade_date.nil?
113
+ head_inner_xml = build_direct_request_x_head('QuerySingleOrder', branch_id, co_no, time_stamp)
114
+ body_inner_xml = "<Date>#{trade_date}</Date><BillNo>#{Util.cmb_bill_no(bill_no)}</BillNo>"
115
+ http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
116
+ SingleOrderMessage.new(http_response)
117
+ end
118
+
119
+ # 商户入账查询接口
120
+ # pos: 当<QryLopFlg>Y</QryLopFlg>续传标记为Y时(表示仍有后续的通讯包,采用多次通讯方式续传时使用)
121
+ # 填写<QryLopBlk>续传包请求数据</QryLopBlk>中的数据
122
+ def self.query_transact(begin_date:, end_date:, count:, operator: nil, pos: nil,
123
+ branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
124
+ query_order_by_cmb_command('QueryTransact', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
125
+ end
126
+
127
+ # 按商户日期查询已结账订单接口
128
+ def self.query_settled_order_by_merchant_date(begin_date:, end_date:, count:, operator: nil, pos: nil,
129
+ branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
130
+ query_order_by_cmb_command('QuerySettledOrderByMerchantDate', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
131
+ end
132
+
133
+ # 按结账日期查询已结账订单接口
134
+ def self.query_settled_order_by_settled_date(begin_date:, end_date:, count:, operator: nil, pos: nil,
135
+ branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
136
+ query_order_by_cmb_command('QuerySettledOrderBySettledDate', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
84
137
  end
85
138
 
86
139
  private_class_method
87
140
 
141
+ def self.query_order_by_cmb_command(cmb_command, begin_date, end_date, count, operator, pos,
142
+ branch_id, co_no, co_key, time_stamp)
143
+ head_inner_xml = build_direct_request_x_head(cmb_command, branch_id, co_no, time_stamp)
144
+ body_inner_xml = build_direct_request_x_query_body(begin_date, end_date, count, operator, pos)
145
+ http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
146
+ BillRecordsMessage.new(http_response)
147
+ end
148
+
149
+ def self.build_direct_request_x_head(cmb_command, branch_id, co_no, time_stamp,
150
+ with_operator: false, operator: nil, operator_password: nil)
151
+ branch_id = CmbPay.branch_id if branch_id.nil?
152
+ co_no = CmbPay.co_no if co_no.nil?
153
+ if with_operator
154
+ operator = CmbPay.operator if operator.nil?
155
+ operator_password = CmbPay.operator_password if operator_password.nil?
156
+ "<BranchNo>#{branch_id}</BranchNo><MerchantNo>#{co_no}</MerchantNo><Operator>#{operator}</Operator><Password>#{operator_password}</Password><TimeStamp>#{Util.cmb_timestamp(t: time_stamp)}</TimeStamp><Command>#{cmb_command}</Command>"
157
+ else
158
+ "<BranchNo>#{branch_id}</BranchNo><MerchantNo>#{co_no}</MerchantNo><TimeStamp>#{Util.cmb_timestamp(t: time_stamp)}</TimeStamp><Command>#{cmb_command}</Command>"
159
+ end
160
+ end
161
+
162
+ def self.build_direct_request_x_query_body(begin_date, end_date, count, operator, pos)
163
+ operator = '9999' if operator.nil?
164
+ begin_date = begin_date.strftime('%Y%m%d') unless begin_date.is_a?(String)
165
+ end_date = end_date.strftime('%Y%m%d') unless end_date.is_a?(String)
166
+ "<BeginDate>#{begin_date}</BeginDate><EndDate>#{end_date}</EndDate><Count>#{count}</Count><Operator>#{operator}</Operator><pos>#{pos}</pos>"
167
+ end
168
+
169
+ def self.hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
170
+ co_key = CmbPay.co_key if co_key.nil?
171
+ hash_input = "#{co_key}#{head_inner_xml}#{body_inner_xml}"
172
+ hash_xml = "<Hash>#{Sign.sha1_digest(hash_input)}</Hash>"
173
+ request_xml = "<Request><Head>#{head_inner_xml}</Head><Body>#{body_inner_xml}</Body>#{hash_xml}</Request>"
174
+ HTTP.post(Service.request_gateway_url(:DirectRequestX), form: { 'Request' => request_xml })
175
+ end
176
+
88
177
  def self.generate_pay_link_of(pay_type, payer_id, bill_no, amount_in_cents, merchant_url, merchant_para,
89
178
  protocol, merchant_ret_url, merchant_ret_para, card_bank, options)
90
179
  branch_id = options.delete(:branch_id) || CmbPay.branch_id
91
180
  co_no = options.delete(:co_no) || CmbPay.co_no
92
181
  co_key = options.delete(:co_key) || CmbPay.co_key
93
- # 定单号,6位或10位长数字,由商户系统生成,一天内不能重复;
94
- cmb_bill_no = format('%010d', bill_no.to_i % 10_000_000_000)
182
+ cmb_bill_no = Util.cmb_bill_no(bill_no)
95
183
  expire_in_minutes = options.delete(:expire_in_minutes) || CmbPay.expire_in_minutes
96
184
  pay_in_yuan, pay_in_cent = amount_in_cents.to_i.divmod(100)
97
185
  pay_amount = "#{pay_in_yuan}.#{format('%02d', pay_in_cent)}"
@@ -100,15 +188,7 @@ module CmbPay
100
188
  payee_id = options.delete(:payee_id) || CmbPay.default_payee_id
101
189
  random = options.delete(:random)
102
190
  if protocol.is_a?(Hash) && !payer_id.nil?
103
- cmb_reserved_xml = {
104
- 'PNo' => protocol['PNo'],
105
- 'TS' => protocol['TS'] || Time.now.strftime('%Y%m%d%H%M%S'),
106
- 'MchNo' => CmbPay.mch_no,
107
- 'Seq' => protocol['Seq'],
108
- 'MUID' => payer_id,
109
- 'URL' => merchant_url,
110
- 'Para' => cmb_merchant_para
111
- }.to_xml(root: 'Protocol', skip_instruct: true, skip_types: true, indent: 0)
191
+ cmb_reserved_xml = "<Protocol><PNo>#{protocol['PNo']}</PNo><TS>#{protocol['TS'] || Time.now.strftime('%Y%m%d%H%M%S')}</TS><MchNo>#{CmbPay.mch_no}</MchNo><Seq>#{protocol['Seq']}</Seq><MUID>#{payer_id}</MUID><URL>#{merchant_url}</URL><Para>#{cmb_merchant_para}</Para></Protocol>"
112
192
  else
113
193
  cmb_reserved_xml = generate_cmb_card_bank_xml(card_bank)
114
194
  payee_id = nil
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmb_pay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Guo
@@ -30,7 +30,7 @@ cert_chain:
30
30
  R5k6Ma92sW8jupX4cqbSu9rntdVQkNRpoHIrfU0MZT0cKsg/D1zMteylxrO3KMsz
31
31
  SPQRv+nrI1J0zevFqb8010heoR8SDyUA0Mm3+Q==
32
32
  -----END CERTIFICATE-----
33
- date: 2016-07-11 00:00:00.000000000 Z
33
+ date: 2016-08-08 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: http
@@ -52,26 +52,6 @@ dependencies:
52
52
  - - "<"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3'
55
- - !ruby/object:Gem::Dependency
56
- name: activesupport
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 4.2.6
62
- - - "<"
63
- - !ruby/object:Gem::Version
64
- version: 5.1.x
65
- type: :runtime
66
- prerelease: false
67
- version_requirements: !ruby/object:Gem::Requirement
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: 4.2.6
72
- - - "<"
73
- - !ruby/object:Gem::Version
74
- version: 5.1.x
75
55
  - !ruby/object:Gem::Dependency
76
56
  name: bundler
77
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +102,7 @@ executables: []
122
102
  extensions: []
123
103
  extra_rdoc_files: []
124
104
  files:
105
+ - ".codeclimate.yml"
125
106
  - ".gitignore"
126
107
  - ".idea/vcs.xml"
127
108
  - ".rspec"
@@ -132,10 +113,12 @@ files:
132
113
  - bin/console
133
114
  - bin/setup
134
115
  - certs/Eric-Guo.pem
135
- - checksum/cmb_pay-0.0.3.gem.sha512
136
116
  - lib/cmb_pay.rb
137
117
  - lib/cmb_pay/merchant_code.rb
138
- - lib/cmb_pay/message.rb
118
+ - lib/cmb_pay/message/bill_records_message.rb
119
+ - lib/cmb_pay/message/pay_message.rb
120
+ - lib/cmb_pay/message/refund_order_message.rb
121
+ - lib/cmb_pay/message/single_order_message.rb
139
122
  - lib/cmb_pay/public.key
140
123
  - lib/cmb_pay/service.rb
141
124
  - lib/cmb_pay/sign.rb
metadata.gz.sig CHANGED
Binary file
@@ -1 +0,0 @@
1
- 49dec10bce7bd6a94b0b80e6539e209a2297bcc6a6fc23699d78d5f2cdd222b36a7ce4aa6f18cfd27afed3d326891938d7279c5e41fecad986f9ea5f6af49443
@@ -1,68 +0,0 @@
1
- module CmbPay
2
- class Message
3
- attr_accessor :succeed # 消息成功失败,成功为'Y',失败为'N'
4
- attr_accessor :co_no # 商户号,6位长数字,由银行在商户开户时确定
5
- attr_accessor :bill_no # 订单号(由支付命令送来);
6
- attr_accessor :amount # 实际支付金额(由支付命令送来)
7
- attr_accessor :date # 订单下单日期(由支付命令送来)
8
- attr_accessor :merchant_para # 商户自定义传递参数(由支付命令送来)
9
- attr_accessor :msg # 银行通知用户支付结构消息
10
-
11
- attr_accessor :branch_id # 分行号
12
- attr_accessor :bank_date # 银行主机交易日期
13
- attr_accessor :bank_serial_no # 银行流水号
14
-
15
- attr_accessor :signature # 通知命令签名
16
-
17
- attr_reader :query_string # 原始的query_string
18
-
19
- def initialize(query_string)
20
- query_string = URI.encode_www_form(query_string) if query_string.is_a? Hash
21
- @query_string = query_string
22
-
23
- params = URI.decode_www_form(query_string).to_h
24
-
25
- @succeed = params['Succeed']
26
- @co_no = params['CoNo']
27
- @bill_no = params['BillNo']
28
- @amount = params['Amount']
29
- @date = params['Date']
30
- @merchant_para = params['MerchantPara']
31
- @msg = params['Msg']
32
-
33
- # 银行通知用户的支付结果消息。信息的前38个字符格式为:4位分行号+6位商户号+8位银行接受交易的日期+20位银行流水号;
34
- # 可以利用交易日期+银行流水号+订单号对该订单进行结帐处理
35
- msg = params['Msg'][0..37]
36
- @branch_id = msg[0..3]
37
- @co_no ||= msg[4..9]
38
- @bank_date = msg[10..17]
39
- @bank_serial_no = msg[18..37]
40
-
41
- @signature = params['Signature']
42
- end
43
-
44
- def valid?
45
- Sign::Sha1WithRsa.verify(query_string)
46
- end
47
-
48
- def succeed?
49
- succeed == 'Y'
50
- end
51
-
52
- def amount_cents
53
- (amount.to_f * 100).to_i
54
- end
55
-
56
- def order_date
57
- Date.strptime(date, '%Y%m%d')
58
- end
59
-
60
- def payment_date
61
- Date.strptime(bank_date, '%Y%m%d')
62
- end
63
-
64
- def merchant_params
65
- URI.decode_www_form(merchant_para.tr('|', '&')).to_h
66
- end
67
- end
68
- end