cmb_pay 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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