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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.codeclimate.yml +18 -0
- data/.rubocop.yml +4 -1
- data/README.md +8 -0
- data/lib/cmb_pay/message/bill_records_message.rb +85 -0
- data/lib/cmb_pay/message/pay_message.rb +81 -0
- data/lib/cmb_pay/message/refund_order_message.rb +34 -0
- data/lib/cmb_pay/message/single_order_message.rb +45 -0
- data/lib/cmb_pay/service.rb +4 -2
- data/lib/cmb_pay/util.rb +15 -2
- data/lib/cmb_pay/version.rb +1 -1
- data/lib/cmb_pay.rb +102 -22
- data.tar.gz.sig +0 -0
- metadata +7 -24
- metadata.gz.sig +0 -0
- data/checksum/cmb_pay-0.0.3.gem.sha512 +0 -1
- data/lib/cmb_pay/message.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cb6e2e16c168adda66116084b9baee9885df88f
|
4
|
+
data.tar.gz: 538974a366f22d25f637d139d1a6422ff368f773
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
data/lib/cmb_pay/service.rb
CHANGED
@@ -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.
|
6
|
+
[str].pack('H*')
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.binary_to_hex(s)
|
8
|
-
s.
|
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
|
data/lib/cmb_pay/version.rb
CHANGED
data/lib/cmb_pay.rb
CHANGED
@@ -1,25 +1,33 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'uri'
|
3
|
-
require '
|
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
|
17
|
-
attr_accessor :
|
18
|
-
attr_accessor :
|
19
|
-
attr_accessor :
|
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.
|
83
|
-
|
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
|
-
|
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
|
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-
|
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
|
data/lib/cmb_pay/message.rb
DELETED
@@ -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
|